Add error-chain errors
authorjluner <justin.j.luner@gmail.com>
Wed, 24 May 2017 04:35:54 +0000 (23:35 -0500)
committerjluner <justin.j.luner@gmail.com>
Thu, 25 May 2017 02:14:04 +0000 (21:14 -0500)
Convert CargoResult, CargoError into an implementation provided by error-chain. The previous is_human machinery is mostly removed; now errors are displayed unless of the Internal kind, verbose mode will print all errors.

56 files changed:
Cargo.lock
Cargo.toml
src/bin/bench.rs
src/bin/cargo.rs
src/bin/locate_project.rs
src/bin/login.rs
src/bin/run.rs
src/bin/test.rs
src/cargo/core/dependency.rs
src/cargo/core/package.rs
src/cargo/core/package_id.rs
src/cargo/core/package_id_spec.rs
src/cargo/core/registry.rs
src/cargo/core/resolver/encode.rs
src/cargo/core/resolver/mod.rs
src/cargo/core/workspace.rs
src/cargo/lib.rs
src/cargo/ops/cargo_clean.rs
src/cargo/ops/cargo_install.rs
src/cargo/ops/cargo_new.rs
src/cargo/ops/cargo_package.rs
src/cargo/ops/cargo_read_manifest.rs
src/cargo/ops/cargo_run.rs
src/cargo/ops/cargo_rustc/context.rs
src/cargo/ops/cargo_rustc/custom_build.rs
src/cargo/ops/cargo_rustc/fingerprint.rs
src/cargo/ops/cargo_rustc/job_queue.rs
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/ops/cargo_test.rs
src/cargo/ops/lockfile.rs
src/cargo/ops/registry.rs
src/cargo/ops/resolve.rs
src/cargo/sources/config.rs
src/cargo/sources/directory.rs
src/cargo/sources/git/utils.rs
src/cargo/sources/path.rs
src/cargo/sources/registry/index.rs
src/cargo/sources/registry/local.rs
src/cargo/sources/registry/mod.rs
src/cargo/sources/registry/remote.rs
src/cargo/sources/replaced.rs
src/cargo/util/cfg.rs
src/cargo/util/config.rs
src/cargo/util/errors.rs
src/cargo/util/flock.rs
src/cargo/util/mod.rs
src/cargo/util/network.rs
src/cargo/util/paths.rs
src/cargo/util/process_builder.rs
src/cargo/util/rustc.rs
src/cargo/util/toml.rs
src/crates-io/Cargo.toml
src/crates-io/lib.rs
tests/cargotest/support/mod.rs
tests/doc.rs
tests/package.rs

index 34ceb1c7fb306bbc7abef47d0580392b0627028d..5db351295e3a54b12b9c9ea1fb615d387ccb3190 100644 (file)
@@ -10,6 +10,7 @@ dependencies = [
  "curl 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "flate2 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "fs2 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -65,6 +66,29 @@ dependencies = [
  "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "backtrace"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "backtrace-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "backtrace-sys"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "bitflags"
 version = "0.7.0"
@@ -121,6 +145,7 @@ name = "crates-io"
 version = "0.9.0"
 dependencies = [
  "curl 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -157,6 +182,15 @@ dependencies = [
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "dbghelp-sys"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "docopt"
 version = "0.7.0"
@@ -182,6 +216,14 @@ dependencies = [
  "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "error-chain"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "filetime"
 version = "0.1.10"
@@ -559,6 +601,11 @@ name = "regex-syntax"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "rustc-demangle"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "rustc-serialize"
 version = "0.3.24"
@@ -799,6 +846,8 @@ dependencies = [
 "checksum advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a"
 "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
 "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
+"checksum backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72f9b4182546f4b04ebc4ab7f84948953a118bd6021a1b6a6c909e3e94f6be76"
+"checksum backtrace-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d192fd129132fbc97497c1f2ec2c2c5174e376b95f535199ef4fe0a293d33842"
 "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
 "checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
 "checksum bufstream 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f382711e76b9de6c744cc00d0497baba02fb00a787f088c879f01d09468e32"
@@ -807,9 +856,11 @@ dependencies = [
 "checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97"
 "checksum curl 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c90e1240ef340dd4027ade439e5c7c2064dd9dc652682117bd50d1486a3add7b"
 "checksum curl-sys 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "23e7e544dc5e1ba42c4a4a678bd47985e84b9c3f4d3404c29700622a029db9c3"
+"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
 "checksum docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab32ea6e284d87987066f21a9e809a73c14720571ef34516f0890b3d355ccfd8"
 "checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90"
 "checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83"
+"checksum error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8"
 "checksum filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5363ab8e4139b8568a6237db5248646e5a8a2f89bd5ccb02092182b11fd3e922"
 "checksum flate2 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)" = "36df0166e856739905cd3d7e0b210fe818592211a008862599845e012d8d304c"
 "checksum foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e4056b9bd47f8ac5ba12be771f77a0dae796d1bbaaf5fd0b9c2d38b69b8a29d"
@@ -854,6 +905,7 @@ dependencies = [
 "checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01"
 "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
 "checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457"
+"checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95"
 "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
 "checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"
 "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
index 8bad427e6649fe151ce6a0aaabce42e04cdd5504..1e400dc8838e3af8d8d0c4fc5b0468cc2a7fc05e 100644 (file)
@@ -22,6 +22,7 @@ crossbeam = "0.2"
 curl = "0.4.6"
 docopt = "0.7"
 env_logger = "0.4"
+error-chain = "0.10.0"
 filetime = "0.1"
 flate2 = "0.2"
 fs2 = "0.4"
index 6fa70f2b998d408f431347d1ea16ee89f432709d..6b2b8166638baadb2bccf0a038f5717be2990f24 100644 (file)
@@ -1,6 +1,6 @@
 use cargo::core::Workspace;
 use cargo::ops::{self, MessageFormat, Packages};
-use cargo::util::{CliResult, CliError, Human, Config, human};
+use cargo::util::{CliResult, CliError, Config, human, CargoErrorKind};
 use cargo::util::important_paths::{find_root_manifest_for_wd};
 
 #[derive(RustcDecodable)]
@@ -129,7 +129,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult {
         Some(err) => {
             Err(match err.exit.as_ref().and_then(|e| e.code()) {
                 Some(i) => CliError::new(human("bench failed"), i),
-                None => CliError::new(Box::new(Human(err)), 101)
+                None => CliError::new(CargoErrorKind::CargoTestErrorKind(err).into(), 101)
             })
         }
     }
index 1b8c7a9c3d14d9aa13a565af49485f0d5e63f132..73f7f81953f19ffbaa6948ab321e202720381473 100644 (file)
@@ -17,7 +17,7 @@ use std::fs;
 use std::path::{Path, PathBuf};
 
 use cargo::core::shell::{Verbosity, ColorConfig};
-use cargo::util::{self, CliResult, lev_distance, Config, human, CargoResult};
+use cargo::util::{self, CliResult, lev_distance, Config, human, CargoResult, CargoError, CargoErrorKind};
 use cargo::util::CliError;
 
 #[derive(RustcDecodable)]
@@ -330,11 +330,12 @@ fn execute_external_subcommand(config: &Config, cmd: &str, args: &[String]) -> C
         Err(e) => e,
     };
 
-    if let Some(code) = err.exit.as_ref().and_then(|c| c.code()) {
-        Err(CliError::code(code))
-    } else {
-        Err(CliError::new(Box::new(err), 101))
+    if let CargoError(CargoErrorKind::ProcessErrorKind(ref perr), ..) = err {
+        if let Some(code) = perr.exit.as_ref().and_then(|c| c.code()) {
+            return Err(CliError::code(code));
+        }
     }
+    Err(CliError::new(err, 101))    
 }
 
 /// List all runnable commands
index 810be467c0a326b8912ed2135f38224d0ad1ea6a..a7d83b4be8d6a074e0e9c657279667dad7ca7d9e 100644 (file)
@@ -1,5 +1,5 @@
 use cargo;
-use cargo::util::{CliResult, CliError, human, ChainError, Config};
+use cargo::util::{CliResult, CliError, human, Config};
 use cargo::util::important_paths::{find_root_manifest_for_wd};
 
 #[derive(RustcDecodable)]
@@ -28,7 +28,7 @@ pub fn execute(flags: LocateProjectFlags,
     let root = find_root_manifest_for_wd(flags.flag_manifest_path, config.cwd())?;
 
     let string = root.to_str()
-                      .chain_error(|| human("Your project path contains \
+                      .ok_or_else(|| human("Your project path contains \
                                              characters not representable in \
                                              Unicode"))
                       .map_err(|e| CliError::new(e, 1))?;
index 7e575f47df41f9005bb7bf8723521e61499294db..75a8fd7f848e0e42d4b71158aa127861434d0053 100644 (file)
@@ -4,7 +4,7 @@ use std::io;
 use cargo::ops;
 use cargo::core::{SourceId, Source};
 use cargo::sources::RegistrySource;
-use cargo::util::{CliResult, Config, human, ChainError};
+use cargo::util::{CliResult, CargoResultExt, Config, human};
 
 #[derive(RustcDecodable)]
 pub struct Options {
@@ -51,7 +51,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult {
             println!("please visit {}me and paste the API Token below", host);
             let mut line = String::new();
             let input = io::stdin();
-            input.lock().read_line(&mut line).chain_error(|| {
+            input.lock().read_line(&mut line).chain_err(|| {
                 human("failed to read stdin")
             })?;
             line
index 6d66d26ee4e7c0ef9578ad79afbc34cd0a0ff860..61023fb5e5c6f2dddfd9a706498f3bc4177848ea 100644 (file)
@@ -2,7 +2,7 @@ use std::iter::FromIterator;
 
 use cargo::core::Workspace;
 use cargo::ops::{self, MessageFormat, Packages};
-use cargo::util::{CliResult, CliError, Config, Human};
+use cargo::util::{CliResult, CliError, Config, CargoErrorKind};
 use cargo::util::important_paths::{find_root_manifest_for_wd};
 
 #[derive(RustcDecodable)]
@@ -113,7 +113,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult {
             // bad and we always want to forward that up.
             let exit = match err.exit.clone() {
                 Some(exit) => exit,
-                None => return Err(CliError::new(Box::new(Human(err)), 101)),
+                None => return Err(CliError::new(CargoErrorKind::ProcessErrorKind(err).into(), 101)),
             };
 
             // If `-q` was passed then we suppress extra error information about
@@ -123,7 +123,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult {
             Err(if options.flag_quiet == Some(true) {
                 CliError::code(exit_code)
             } else {
-                CliError::new(Box::new(Human(err)), exit_code)
+                CliError::new(CargoErrorKind::ProcessErrorKind(err).into(), exit_code)
             })
         }
     }
index 817c2bcc35d1e2d9d70dd937307282df26332e02..22e278043dbd397f05f808f225011b2b7234ac6f 100644 (file)
@@ -1,6 +1,6 @@
 use cargo::core::Workspace;
 use cargo::ops::{self, MessageFormat, Packages};
-use cargo::util::{CliResult, CliError, Human, human, Config};
+use cargo::util::{CliResult, CliError, human, Config, CargoErrorKind};
 use cargo::util::important_paths::find_root_manifest_for_wd;
 
 #[derive(RustcDecodable)]
@@ -164,7 +164,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult {
         Some(err) => {
             Err(match err.exit.as_ref().and_then(|e| e.code()) {
                 Some(i) => CliError::new(human(err.hint()), i),
-                None => CliError::new(Box::new(Human(err)), 101),
+                None => CliError::new(CargoErrorKind::CargoTestErrorKind(err).into(), 101),
             })
         }
     }
index 9401eeccbb0a8f5b9dccd6d85f4c3462015e9dcf..ff8d3976c4d808cf1552b2c49c2fbdb56d6d97c1 100644 (file)
@@ -7,7 +7,8 @@ use semver::ReqParseError;
 use serde::ser;
 
 use core::{SourceId, Summary, PackageId};
-use util::{CargoError, CargoResult, Cfg, CfgExpr, ChainError, human, Config};
+use util::{Cfg, CfgExpr, human, Config};
+use util::errors::{CargoResult, CargoResultExt, CargoError};
 
 /// Information about a dependency requested by a Cargo manifest.
 /// Cheap to copy.
@@ -145,7 +146,7 @@ this warning.
 
                         Ok(requirement)
                     }
-                    e => Err(From::from(e)),
+                    e => Err(e.into()),
                 }
             },
             Ok(v) => Ok(v),
@@ -361,12 +362,12 @@ impl ser::Serialize for Platform {
 }
 
 impl FromStr for Platform {
-    type Err = Box<CargoError>;
+    type Err = CargoError;
 
     fn from_str(s: &str) -> CargoResult<Platform> {
         if s.starts_with("cfg(") && s.ends_with(")") {
             let s = &s[4..s.len()-1];
-            s.parse().map(Platform::Cfg).chain_error(|| {
+            s.parse().map(Platform::Cfg).chain_err(|| {
                 human(format!("failed to parse `{}` as a cfg expression", s))
             })
         } else {
index 943f570d335c9cdb15d6eca4168bafc2dd384a66..00cbd222d369f891cfbf757f82200da90fffd7cc 100644 (file)
@@ -11,7 +11,8 @@ use toml;
 use core::{Dependency, Manifest, PackageId, SourceId, Target};
 use core::{Summary, SourceMap};
 use ops;
-use util::{CargoResult, Config, LazyCell, ChainError, internal, human, lev_distance};
+use util::{Config, LazyCell, internal, human, lev_distance};
+use util::errors::{CargoResult, CargoResultExt};
 
 /// Information about a package that is available somewhere in the file system.
 ///
@@ -181,7 +182,7 @@ impl<'cfg> PackageSet<'cfg> {
     }
 
     pub fn get(&self, id: &PackageId) -> CargoResult<&Package> {
-        let slot = self.packages.iter().find(|p| p.0 == *id).chain_error(|| {
+        let slot = self.packages.iter().find(|p| p.0 == *id).ok_or_else(|| {
             internal(format!("couldn't find `{}` in package set", id))
         })?;
         let slot = &slot.1;
@@ -189,10 +190,10 @@ impl<'cfg> PackageSet<'cfg> {
             return Ok(pkg)
         }
         let mut sources = self.sources.borrow_mut();
-        let source = sources.get_mut(id.source_id()).chain_error(|| {
+        let source = sources.get_mut(id.source_id()).ok_or_else(|| {
             internal(format!("couldn't find source for `{}`", id))
         })?;
-        let pkg = source.download(id).chain_error(|| {
+        let pkg = source.download(id).chain_err(|| {
             human("unable to get packages from source")
         })?;
         assert!(slot.fill(pkg).is_ok());
index f7d67300a9fd9246fa80c7ee464d3002c8af8aef..1e906d97d1613e10f4fd82ec741436019e0cd3e2 100644 (file)
@@ -1,5 +1,4 @@
 use std::cmp::Ordering;
-use std::error::Error;
 use std::fmt::{self, Formatter};
 use std::hash::Hash;
 use std::hash;
@@ -9,7 +8,7 @@ use semver;
 use serde::de;
 use serde::ser;
 
-use util::{CargoResult, CargoError, ToSemver};
+use util::{CargoResult, ToSemver};
 use core::source::SourceId;
 
 /// Identifier for a specific version of a package in a specific source.
@@ -96,41 +95,10 @@ impl Ord for PackageId {
     }
 }
 
-#[derive(Clone, Debug, PartialEq)]
-pub enum PackageIdError {
-    InvalidVersion(String),
-    InvalidNamespace(String)
-}
-
-impl Error for PackageIdError {
-    fn description(&self) -> &str { "failed to parse package id" }
-}
-
-impl fmt::Display for PackageIdError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            PackageIdError::InvalidVersion(ref v) => {
-                write!(f, "invalid version: {}", *v)
-            }
-            PackageIdError::InvalidNamespace(ref ns) => {
-                write!(f, "invalid namespace: {}", *ns)
-            }
-        }
-    }
-}
-
-impl CargoError for PackageIdError {
-    fn is_human(&self) -> bool { true }
-}
-
-impl From<PackageIdError> for Box<CargoError> {
-    fn from(t: PackageIdError) -> Box<CargoError> { Box::new(t) }
-}
-
 impl PackageId {
     pub fn new<T: ToSemver>(name: &str, version: T,
                              sid: &SourceId) -> CargoResult<PackageId> {
-        let v = version.to_semver().map_err(PackageIdError::InvalidVersion)?;
+        let v = version.to_semver()?;
         Ok(PackageId {
             inner: Arc::new(PackageIdInner {
                 name: name.to_string(),
index 2af87fb6c01bdea1876c4bdad385254e0574bcb5..ad56601386dd68a56c577e1ded309b77f7f2726d 100644 (file)
@@ -5,7 +5,8 @@ use semver::Version;
 use url::Url;
 
 use core::PackageId;
-use util::{CargoResult, ToUrl, human, ToSemver, ChainError};
+use util::{ToUrl, human, ToSemver};
+use util::errors::{CargoResult, CargoResultExt};
 
 #[derive(Clone, PartialEq, Eq, Debug)]
 pub struct PackageIdSpec {
@@ -47,7 +48,7 @@ impl PackageIdSpec {
     pub fn query_str<'a, I>(spec: &str, i: I) -> CargoResult<&'a PackageId>
         where I: IntoIterator<Item=&'a PackageId>
     {
-        let spec = PackageIdSpec::parse(spec).chain_error(|| {
+        let spec = PackageIdSpec::parse(spec).chain_err(|| {
             human(format!("invalid package id specification: `{}`", spec))
         })?;
         spec.query(i)
@@ -68,10 +69,10 @@ impl PackageIdSpec {
         let frag = url.fragment().map(|s| s.to_owned());
         url.set_fragment(None);
         let (name, version) = {
-            let mut path = url.path_segments().chain_error(|| {
+            let mut path = url.path_segments().ok_or_else(|| {
                 human(format!("pkgid urls must have a path: {}", url))
             })?;
-            let path_name = path.next_back().chain_error(|| {
+            let path_name = path.next_back().ok_or_else(|| {
                 human(format!("pkgid urls must have at least one path \
                                component: {}", url))
             })?;
index 292a662ceabe6ab415ab3bdb1b2d9a4b3614be5a..0929aa9010497d432ae851f4d0af6af03bc4705e 100644 (file)
@@ -2,7 +2,8 @@ use std::collections::HashMap;
 
 use core::{Source, SourceId, SourceMap, Summary, Dependency, PackageId, Package};
 use core::PackageSet;
-use util::{CargoResult, ChainError, Config, human, profile};
+use util::{Config, human, profile};
+use util::errors::{CargoResult, CargoResultExt};
 use sources::config::SourceConfigMap;
 
 /// Source of information about a group of packages.
@@ -189,7 +190,7 @@ impl<'cfg> PackageRegistry<'cfg> {
             // Ensure the source has fetched all necessary remote data.
             let _p = profile::start(format!("updating: {}", source_id));
             self.sources.get_mut(source_id).unwrap().update()
-        }).chain_error(|| human(format!("Unable to update {}", source_id)))
+        })().chain_err(|| human(format!("Unable to update {}", source_id)))
     }
 
     fn query_overrides(&mut self, dep: &Dependency)
@@ -336,7 +337,7 @@ http://doc.crates.io/specifying-dependencies.html#overriding-dependencies
 impl<'cfg> Registry for PackageRegistry<'cfg> {
     fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
         // Ensure the requested source_id is loaded
-        self.ensure_loaded(dep.source_id(), Kind::Normal).chain_error(|| {
+        self.ensure_loaded(dep.source_id(), Kind::Normal).chain_err(|| {
             human(format!("failed to load source for a dependency \
                            on `{}`", dep.name()))
         })?;
index 61f8daa52feea2a38cbb6a4c3758ddd9861f8264..403c01e51c45f888089ebdc1ac8c71516f6a540b 100644 (file)
@@ -6,7 +6,8 @@ use serde::ser;
 use serde::de;
 
 use core::{Package, PackageId, SourceId, Workspace};
-use util::{CargoResult, Graph, Config, internal, ChainError, CargoError};
+use util::{Graph, Config, internal};
+use util::errors::{CargoResult, CargoResultExt, CargoError};
 
 use super::Resolve;
 
@@ -132,7 +133,7 @@ impl EncodableResolve {
         for (k, v) in metadata.iter().filter(|p| p.0.starts_with(prefix)) {
             to_remove.push(k.to_string());
             let k = &k[prefix.len()..];
-            let enc_id: EncodablePackageId = k.parse().chain_error(|| {
+            let enc_id: EncodablePackageId = k.parse().chain_err(|| {
                 internal("invalid encoding of checksum in lockfile")
             })?;
             let id = match lookup_id(&enc_id) {
@@ -235,12 +236,12 @@ impl fmt::Display for EncodablePackageId {
 }
 
 impl FromStr for EncodablePackageId {
-    type Err = Box<CargoError>;
+    type Err = CargoError;
 
     fn from_str(s: &str) -> CargoResult<EncodablePackageId> {
         let mut s = s.splitn(3, ' ');
         let name = s.next().unwrap();
-        let version = s.next().chain_error(|| {
+        let version = s.next().ok_or_else(|| {
             internal("invalid serialized PackageId")
         })?;
         let source_id = match s.next() {
index 896b280469e6fea9c04dd02ef1cb518d4ae779bc..f47f5d8e0e3c66d45005e593216482778406e829 100644 (file)
@@ -56,9 +56,9 @@ use semver;
 
 use core::{PackageId, Registry, SourceId, Summary, Dependency};
 use core::PackageIdSpec;
-use util::{CargoResult, Graph, human, CargoError};
+use util::{Graph, human};
+use util::errors::{CargoResult, CargoError};
 use util::profile;
-use util::ChainError;
 use util::graph::{Nodes, Edges};
 
 pub use self::encode::{EncodableResolve, EncodableDependency, EncodablePackageId};
@@ -600,7 +600,7 @@ fn activation_error(cx: &Context,
                     parent: &Summary,
                     dep: &Dependency,
                     prev_active: &[Rc<Summary>],
-                    candidates: &[Candidate]) -> Box<CargoError> {
+                    candidates: &[Candidate]) -> CargoError {
     if candidates.len() > 0 {
         let mut msg = format!("failed to select a version for `{}` \
                                (required by `{}`):\n\
@@ -887,7 +887,7 @@ impl<'a> Context<'a> {
             debug!("found an override for {} {}", dep.name(), dep.version_req());
 
             let mut summaries = registry.query(dep)?.into_iter();
-            let s = summaries.next().chain_error(|| {
+            let s = summaries.next().ok_or_else(|| {
                 human(format!("no matching package for override `{}` found\n\
                                location searched: {}\n\
                                version required: {}",
index 6486233a8d02972c68717f71aef72fcca37dd3b4..cae6bc04a90f7b83abac05d1c343a72eacbfaf23 100644 (file)
@@ -8,7 +8,8 @@ use glob::glob;
 use core::{Package, VirtualManifest, EitherManifest, SourceId};
 use core::{PackageIdSpec, Dependency, Profile, Profiles};
 use ops;
-use util::{Config, CargoResult, Filesystem, human, ChainError};
+use util::{Config, Filesystem, human};
+use util::errors::{CargoResult, CargoResultExt};
 use util::paths;
 
 /// The core abstraction in Cargo for working with a workspace of crates.
@@ -551,11 +552,11 @@ fn expand_member_path(path: &Path) -> CargoResult<Vec<PathBuf>> {
         Some(p) => p,
         None => return Ok(Vec::new()),
     };
-    let res = glob(path).chain_error(|| {
+    let res = glob(path).chain_err(|| {
         human(format!("could not parse pattern `{}`", &path))
     })?;
     res.map(|p| {
-        p.chain_error(|| {
+        p.chain_err(|| {
             human(format!("unable to match path to pattern `{}`", &path))
         })
     }).collect()
index 8403b9a923c6f8dfd3da5ac32645ae43b1a0287f..70bf603f265fe7a58f2557531be1f26add9f21fa 100755 (executable)
@@ -1,7 +1,9 @@
 #![deny(unused)]
 #![cfg_attr(test, deny(warnings))]
+#![recursion_limit="128"]
 
 #[cfg(test)] extern crate hamcrest;
+#[macro_use] extern crate error_chain;
 #[macro_use] extern crate log;
 #[macro_use] extern crate serde_derive;
 #[macro_use] extern crate serde_json;
@@ -30,6 +32,8 @@ extern crate url;
 
 use std::io;
 use std::fmt;
+use std::error::Error;
+use error_chain::ChainedError;
 use rustc_serialize::Decodable;
 use serde::ser;
 use docopt::Docopt;
@@ -38,7 +42,7 @@ use core::{Shell, MultiShell, ShellConfig, Verbosity, ColorConfig};
 use core::shell::Verbosity::{Verbose};
 use term::color::{BLACK};
 
-pub use util::{CargoError, CargoResult, CliError, CliResult, human, Config, ChainError};
+pub use util::{CargoError, CargoErrorKind, CargoResult, CliError, CliResult, human, Config};
 
 pub const CARGO_ENV: &'static str = "CARGO";
 
@@ -186,7 +190,7 @@ pub fn exit_with_error(err: CliError, shell: &mut MultiShell) -> ! {
             shell.say(&error, BLACK)
         };
 
-        if !handle_cause(&error, shell) || hide {
+        if !handle_cause(error, shell) || hide {
             let _ = shell.err().say("\nTo learn more, run the command again \
                                      with --verbose.".to_string(), BLACK);
         }
@@ -195,37 +199,49 @@ pub fn exit_with_error(err: CliError, shell: &mut MultiShell) -> ! {
     std::process::exit(exit_code)
 }
 
-pub fn handle_error(err: &CargoError, shell: &mut MultiShell) {
-    debug!("handle_error; err={:?}", err);
+pub fn handle_error(err: CargoError, shell: &mut MultiShell) {
+    debug!("handle_error; err={:?}", &err);
 
-    let _ignored_result = shell.error(err);
+    let _ignored_result = shell.error(&err);
     handle_cause(err, shell);
 }
 
-fn handle_cause(mut cargo_err: &CargoError, shell: &mut MultiShell) -> bool {
-    let verbose = shell.get_verbose();
-    let mut err;
-    loop {
-        cargo_err = match cargo_err.cargo_cause() {
-            Some(cause) => cause,
-            None => { err = cargo_err.cause(); break }
-        };
-        if verbose != Verbose && !cargo_err.is_human() { return false }
-        print(cargo_err.to_string(), shell);
-    }
-    loop {
-        let cause = match err { Some(err) => err, None => return true };
-        if verbose != Verbose { return false }
-        print(cause.to_string(), shell);
-        err = cause.cause();
-    }
-
+fn handle_cause<E, EKind>(cargo_err: E, shell: &mut MultiShell) -> bool where E: ChainedError<ErrorKind=EKind> + 'static {
     fn print(error: String, shell: &mut MultiShell) {
         let _ = shell.err().say("\nCaused by:", BLACK);
         let _ = shell.err().say(format!("  {}", error), BLACK);
     }
+
+    unsafe fn extend_lifetime(r: &Error) -> &'static Error {
+        std::mem::transmute::<&Error, &'static Error>(r)    
+    }
+
+    let verbose = shell.get_verbose();
+
+    if verbose == Verbose {
+        //The first error has already been printed to the shell
+        //Print all remaining errors
+        for err in cargo_err.iter().skip(1) {
+            print(err.to_string(), shell);
+        }
+    }
+    else {
+        //The first error has already been printed to the shell
+        //Print remaining errors until one marked as Internal appears
+        for err in cargo_err.iter().skip(1) {
+            let err = unsafe { extend_lifetime(err) };
+            if let Some(&CargoError(CargoErrorKind::Internal(..), ..)) = err.downcast_ref::<CargoError>() {
+                return false;
+            }
+
+            print(err.to_string(), shell);
+        }
+    }
+
+    true
 }
 
+
 pub fn version() -> VersionInfo {
     macro_rules! env_str {
         ($name:expr) => { env!($name).to_string() }
index b7c214a75d9da54328593c45f403c33e11971c99..5d7af68527d6f1c5f38eed085fdd7d9a384b8fcc 100644 (file)
@@ -3,7 +3,8 @@ use std::fs;
 use std::path::Path;
 
 use core::{Profiles, Workspace};
-use util::{CargoResult, human, ChainError, Config};
+use util::{human, Config};
+use util::errors::{CargoResult, CargoResultExt};
 use ops::{self, Context, BuildConfig, Kind, Unit};
 
 pub struct CleanOptions<'a> {
@@ -95,11 +96,11 @@ pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> {
 fn rm_rf(path: &Path) -> CargoResult<()> {
     let m = fs::metadata(path);
     if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) {
-        fs::remove_dir_all(path).chain_error(|| {
+        fs::remove_dir_all(path).chain_err(|| {
             human("could not remove build directory")
         })?;
     } else if m.is_ok() {
-        fs::remove_file(path).chain_error(|| {
+        fs::remove_file(path).chain_err(|| {
             human("failed to remove build artifact")
         })?;
     }
index d19ac402faa895e700873024206cb8c9244e57eb..bc77ddc961dea5e37ace811f0f3e39a66c733032 100644 (file)
@@ -16,8 +16,9 @@ use core::{SourceId, Source, Package, Dependency, PackageIdSpec};
 use core::{PackageId, Workspace};
 use ops::{self, CompileFilter, DefaultExecutor};
 use sources::{GitSource, PathSource, SourceConfigMap};
-use util::{CargoResult, ChainError, Config, human, internal};
+use util::{Config, human, internal};
 use util::{Filesystem, FileLock};
+use util::errors::{CargoResult, CargoResultExt};
 
 #[derive(Deserialize, Serialize)]
 #[serde(untagged)]
@@ -69,7 +70,7 @@ pub fn install(root: Option<&str>,
         let path = source_id.url().to_file_path().ok()
                             .expect("path sources must have a valid path");
         let mut src = PathSource::new(&path, source_id, config);
-        src.update().chain_error(|| {
+        src.update().chain_err(|| {
             human(format!("`{}` is not a crate root; specify a crate to \
                            install from crates.io, or use --path or --git to \
                            specify an alternate source", path.display()))
@@ -117,7 +118,7 @@ pub fn install(root: Option<&str>,
     let compile = ops::compile_ws(&ws,
                                   Some(source),
                                   opts,
-                                  Arc::new(DefaultExecutor)).chain_error(|| {
+                                  Arc::new(DefaultExecutor)).chain_err(|| {
         if let Some(td) = td_opt.take() {
             // preserve the temporary directory, so the user can inspect it
             td.into_path();
@@ -159,7 +160,7 @@ pub fn install(root: Option<&str>,
                 continue
             }
         }
-        fs::copy(src, &dst).chain_error(|| {
+        fs::copy(src, &dst).chain_err(|| {
             human(format!("failed to copy `{}` to `{}`", src.display(),
                           dst.display()))
         })?;
@@ -176,7 +177,7 @@ pub fn install(root: Option<&str>,
         let src = staging_dir.path().join(bin);
         let dst = dst.join(bin);
         config.shell().status("Installing", dst.display())?;
-        fs::rename(&src, &dst).chain_error(|| {
+        fs::rename(&src, &dst).chain_err(|| {
             human(format!("failed to move `{}` to `{}`", src.display(),
                           dst.display()))
         })?;
@@ -192,7 +193,7 @@ pub fn install(root: Option<&str>,
                 let src = staging_dir.path().join(bin);
                 let dst = dst.join(bin);
                 config.shell().status("Replacing", dst.display())?;
-                fs::rename(&src, &dst).chain_error(|| {
+                fs::rename(&src, &dst).chain_err(|| {
                     human(format!("failed to move `{}` to `{}`", src.display(),
                                   dst.display()))
                 })?;
@@ -234,7 +235,7 @@ pub fn install(root: Option<&str>,
     match write_result {
         // Replacement error (if any) isn't actually caused by write error
         // but this seems to be the only way to show both.
-        Err(err) => result.chain_error(|| err)?,
+        Err(err) => result.chain_err(|| err)?,
         Ok(_) => result?,
     }
 
@@ -429,7 +430,7 @@ fn read_crate_list(mut file: &File) -> CargoResult<CrateListingV1> {
     (|| -> CargoResult<_> {
         let mut contents = String::new();
         file.read_to_string(&mut contents)?;
-        let listing = toml::from_str(&contents).chain_error(|| {
+        let listing = toml::from_str(&contents).chain_err(|| {
             internal("invalid TOML found for metadata")
         })?;
         match listing {
@@ -438,7 +439,7 @@ fn read_crate_list(mut file: &File) -> CargoResult<CrateListingV1> {
                 Ok(CrateListingV1 { v1: BTreeMap::new() })
             }
         }
-    }).chain_error(|| {
+    })().chain_err(|| {
         human("failed to parse crate metadata")
     })
 }
@@ -450,7 +451,7 @@ fn write_crate_list(mut file: &File, listing: CrateListingV1) -> CargoResult<()>
         let data = toml::to_string(&CrateListing::V1(listing))?;
         file.write_all(data.as_bytes())?;
         Ok(())
-    }).chain_error(|| {
+    })().chain_err(|| {
         human("failed to write crate metadata")
     })
 }
index 5b4f4faaede9b779adbb072081cbbb5f4189c1e8..0f63694a9c641309f35331f3e669a37c9ffde4c8 100644 (file)
@@ -11,8 +11,9 @@ use term::color::BLACK;
 
 use core::Workspace;
 use ops::is_bad_artifact_name;
-use util::{GitRepo, HgRepo, PijulRepo, CargoResult, human, ChainError, internal};
+use util::{GitRepo, HgRepo, PijulRepo, human, internal};
 use util::{Config, paths};
+use util::errors::{CargoResult, CargoResultExt};
 
 use toml;
 
@@ -97,7 +98,7 @@ fn get_name<'a>(path: &'a Path, opts: &'a NewOptions, config: &Config) -> CargoR
                               path.as_os_str());
     }
 
-    let dir_name = path.file_name().and_then(|s| s.to_str()).chain_error(|| {
+    let dir_name = path.file_name().and_then(|s| s.to_str()).ok_or_else(|| {
         human(&format!("cannot create a project with a non-unicode name: {:?}",
                        path.file_name().unwrap()))
     })?;
@@ -285,7 +286,7 @@ pub fn new(opts: NewOptions, config: &Config) -> CargoResult<()> {
         bin: opts.bin,
     };
 
-    mk(config, &mkopts).chain_error(|| {
+    mk(config, &mkopts).chain_err(|| {
         human(format!("Failed to create project `{}` at `{}`",
                       name, path.display()))
     })
@@ -356,7 +357,7 @@ pub fn init(opts: NewOptions, config: &Config) -> CargoResult<()> {
         source_files: src_paths_types,
     };
 
-    mk(config, &mkopts).chain_error(|| {
+    mk(config, &mkopts).chain_err(|| {
         human(format!("Failed to create project `{}` at `{}`",
                       name, path.display()))
     })
index de788bab5b60e2e558b11e0050762dbaf36b07b8..f9a070797bc1534377adb115da4428f34e017fc1 100644 (file)
@@ -11,7 +11,8 @@ use tar::{Archive, Builder, Header, EntryType};
 
 use core::{Package, Workspace, Source, SourceId};
 use sources::PathSource;
-use util::{self, CargoResult, human, internal, ChainError, Config, FileLock};
+use util::{self, human, internal, Config, FileLock};
+use util::errors::{CargoResult, CargoResultExt};
 use ops::{self, DefaultExecutor};
 
 pub struct PackageOpts<'cfg> {
@@ -67,12 +68,12 @@ pub fn package(ws: &Workspace,
     // it exists.
     config.shell().status("Packaging", pkg.package_id().to_string())?;
     dst.file().set_len(0)?;
-    tar(ws, &src, dst.file(), &filename).chain_error(|| {
+    tar(ws, &src, dst.file(), &filename).chain_err(|| {
         human("failed to prepare local package for uploading")
     })?;
     if opts.verify {
         dst.seek(SeekFrom::Start(0))?;
-        run_verify(ws, dst.file(), opts).chain_error(|| {
+        run_verify(ws, dst.file(), opts).chain_err(|| {
             human("failed to verify package tarball")
         })?
     }
@@ -80,7 +81,7 @@ pub fn package(ws: &Workspace,
     {
         let src_path = dst.path();
         let dst_path = dst.parent().join(&filename);
-        fs::rename(&src_path, &dst_path).chain_error(|| {
+        fs::rename(&src_path, &dst_path).chain_err(|| {
             human("failed to move temporary tarball into final location")
         })?;
     }
@@ -198,7 +199,7 @@ fn tar(ws: &Workspace,
     for file in src.list_files(pkg)?.iter() {
         let relative = util::without_prefix(&file, &root).unwrap();
         check_filename(relative)?;
-        let relative = relative.to_str().chain_error(|| {
+        let relative = relative.to_str().ok_or_else(|| {
             human(format!("non-utf8 path in source directory: {}",
                           relative.display()))
         })?;
@@ -227,13 +228,13 @@ fn tar(ws: &Workspace,
         // unpack the selectors 0.4.0 crate on crates.io. Either that or take a
         // look at rust-lang/cargo#2326
         let mut header = Header::new_ustar();
-        header.set_path(&path).chain_error(|| {
+        header.set_path(&path).chain_err(|| {
             human(format!("failed to add to archive: `{}`", relative))
         })?;
-        let mut file = File::open(file).chain_error(|| {
+        let mut file = File::open(file).chain_err(|| {
             human(format!("failed to open for archiving: `{}`", file.display()))
         })?;
-        let metadata = file.metadata().chain_error(|| {
+        let metadata = file.metadata().chain_err(|| {
             human(format!("could not learn metadata for: `{}`", relative))
         })?;
         header.set_metadata(&metadata);
@@ -242,7 +243,7 @@ fn tar(ws: &Workspace,
             let orig = Path::new(&path).with_file_name("Cargo.toml.orig");
             header.set_path(&orig)?;
             header.set_cksum();
-            ar.append(&header, &mut file).chain_error(|| {
+            ar.append(&header, &mut file).chain_err(|| {
                 internal(format!("could not archive source file `{}`", relative))
             })?;
 
@@ -253,12 +254,12 @@ fn tar(ws: &Workspace,
             header.set_mode(0o644);
             header.set_size(toml.len() as u64);
             header.set_cksum();
-            ar.append(&header, toml.as_bytes()).chain_error(|| {
+            ar.append(&header, toml.as_bytes()).chain_err(|| {
                 internal(format!("could not archive source file `{}`", relative))
             })?;
         } else {
             header.set_cksum();
-            ar.append(&header, &mut file).chain_error(|| {
+            ar.append(&header, &mut file).chain_err(|| {
                 internal(format!("could not archive source file `{}`", relative))
             })?;
         }
index 09f8dec855abf2ea144443b2d20a57efd84be560..4933e0ea672d9056639c0587cb26649d6d99107f 100644 (file)
@@ -4,7 +4,8 @@ use std::io;
 use std::path::{Path, PathBuf};
 
 use core::{Package, SourceId, PackageId, EitherManifest};
-use util::{self, paths, CargoResult, human, Config, ChainError};
+use util::{self, paths, human, Config};
+use util::errors::{CargoResult, CargoResultExt};
 use util::important_paths::find_project_manifest_exact;
 use util::toml::Layout;
 
@@ -15,7 +16,7 @@ pub fn read_manifest(path: &Path, source_id: &SourceId, config: &Config)
 
     let layout = Layout::from_project_path(path.parent().unwrap());
     let root = layout.root.clone();
-    util::toml::to_manifest(&contents, source_id, layout, config).chain_error(|| {
+    util::toml::to_manifest(&contents, source_id, layout, config).chain_err(|| {
         human(format!("failed to parse manifest at `{}`",
                       root.join("Cargo.toml").display()))
     })
@@ -94,7 +95,7 @@ fn walk(path: &Path, callback: &mut FnMut(&Path) -> CargoResult<bool>)
             return Ok(())
         }
         Err(e) => {
-            return Err(human(e)).chain_error(|| {
+            return Err(human(e)).chain_err(|| {
                 human(format!("failed to read directory `{}`", path.display()))
             })
         }
index 47d18a981716945ea987f9973e48cb82a8341334..2c823e21977458219cdc6cae95a16b6d1d9289e3 100644 (file)
@@ -1,7 +1,8 @@
 use std::path::Path;
 
 use ops::{self, Packages};
-use util::{self, human, CargoResult, ProcessError};
+use util::{self, human, CargoResult, CargoError, ProcessError};
+use util::errors::CargoErrorKind;
 use core::Workspace;
 
 pub fn run(ws: &Workspace,
@@ -60,5 +61,12 @@ pub fn run(ws: &Workspace,
     process.args(args).cwd(config.cwd());
 
     config.shell().status("Running", process.to_string())?;
-    Ok(process.exec_replace().err())
+
+    let result = process.exec_replace();
+
+    match result {
+        Ok(()) => Ok(None),
+        Err(CargoError(CargoErrorKind::ProcessErrorKind(e), ..)) => Ok(Some(e)),
+        Err(e) => Err(e)
+    }
 }
index 77131455ae6cf5bd06c8497c124d206dc8ab5459..9076b80a60baf72282ea3b8a94e8b14cd2fd2bf6 100644 (file)
@@ -11,7 +11,8 @@ use std::sync::Arc;
 use core::{Package, PackageId, PackageSet, Resolve, Target, Profile};
 use core::{TargetKind, Profiles, Dependency, Workspace};
 use core::dependency::Kind as DepKind;
-use util::{self, CargoResult, ChainError, internal, Config, profile, Cfg, CfgExpr, human};
+use util::{self, internal, Config, profile, Cfg, CfgExpr, human};
+use util::errors::{CargoResult, CargoResultExt};
 
 use super::TargetConfig;
 use super::custom_build::{BuildState, BuildScripts};
@@ -121,12 +122,12 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
     pub fn prepare(&mut self) -> CargoResult<()> {
         let _p = profile::start("preparing layout");
 
-        self.host.prepare().chain_error(|| {
+        self.host.prepare().chain_err(|| {
             internal(format!("couldn't prepare build directories"))
         })?;
         match self.target {
             Some(ref mut target) => {
-                target.prepare().chain_error(|| {
+                target.prepare().chain_err(|| {
                     internal(format!("couldn't prepare build directories"))
                 })?;
             }
@@ -213,7 +214,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
         let output = with_cfg.exec_with_output().or_else(|_| {
             has_cfg_and_sysroot = false;
             process.exec_with_output()
-        }).chain_error(|| {
+        }).chain_err(|| {
             human(format!("failed to run `rustc` to learn about \
                            target-specific information"))
         })?;
index 4514ab42bbb2d15205e1c32aeea9f3bfdcf88b8c..12b87aa33534da80047471a02b4f8c4d2f0778ca 100644 (file)
@@ -5,8 +5,9 @@ use std::str;
 use std::sync::{Mutex, Arc};
 
 use core::PackageId;
-use util::{CargoResult, Human, Freshness, Cfg};
-use util::{internal, ChainError, profile, paths};
+use util::{Freshness, Cfg};
+use util::errors::{CargoResult, CargoResultExt, CargoError};
+use util::{internal, profile, paths};
 use util::machine_message;
 
 use super::job::Work;
@@ -201,7 +202,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>)
         // If we have an old build directory, then just move it into place,
         // otherwise create it!
         if fs::metadata(&build_output).is_err() {
-            fs::create_dir(&build_output).chain_error(|| {
+            fs::create_dir(&build_output).chain_err(|| {
                 internal("failed to create script output directory for \
                           build command")
             })?;
@@ -215,7 +216,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>)
             let build_state = build_state.outputs.lock().unwrap();
             for (name, id) in lib_deps {
                 let key = (id.clone(), kind);
-                let state = build_state.get(&key).chain_error(|| {
+                let state = build_state.get(&key).ok_or_else(|| {
                     internal(format!("failed to locate build state for env \
                                       vars: {}/{:?}", id, kind))
                 })?;
@@ -237,10 +238,10 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>)
         let output = cmd.exec_with_streaming(
             &mut |out_line| { state.stdout(out_line); Ok(()) },
             &mut |err_line| { state.stderr(err_line); Ok(()) },
-        ).map_err(|mut e| {
-            e.desc = format!("failed to run custom build command for `{}`\n{}",
-                             pkg_name, e.desc);
-            Human(e)
+        ).map_err(|e| {
+            let desc = e.description().to_string();
+            CargoError::with_chain(e, format!("failed to run custom build command for `{}`\n{}",
+                             pkg_name, desc))
         })?;
 
         paths::write(&output_file, &output.stdout)?;
index dbae58eec0d6cae316ef7d030de8d9fd722d11a8..1be9d884e95fee9084cccf45077ef92ad8d9f1cf 100644 (file)
@@ -12,7 +12,8 @@ use serde_json;
 
 use core::{Package, TargetKind};
 use util;
-use util::{CargoResult, Fresh, Dirty, Freshness, internal, profile, ChainError};
+use util::{Fresh, Dirty, Freshness, internal, profile};
+use util::errors::{CargoResult, CargoResultExt};
 use util::paths;
 
 use super::job::Work;
@@ -72,7 +73,7 @@ pub fn prepare_target<'a, 'cfg>(cx: &mut Context<'a, 'cfg>,
     if compare.is_err() {
         let source_id = unit.pkg.package_id().source_id();
         let sources = cx.packages.sources();
-        let source = sources.get(source_id).chain_error(|| {
+        let source = sources.get(source_id).ok_or_else(|| {
             internal("missing package source")
         })?;
         source.verify(unit.pkg.package_id())?;
@@ -180,9 +181,10 @@ impl Fingerprint {
     fn update_local(&self) -> CargoResult<()> {
         match self.local {
             LocalFingerprint::MtimeBased(ref slot, ref path) => {
-                let meta = fs::metadata(path).chain_error(|| {
-                    internal(format!("failed to stat `{}`", path.display()))
-                })?;
+                let meta = fs::metadata(path)
+                    .chain_err(|| {
+                        internal(format!("failed to stat `{}`", path.display()))
+                    })?;
                 let mtime = FileTime::from_last_modification_time(&meta);
                 *slot.0.lock().unwrap() = Some(mtime);
             }
@@ -506,26 +508,20 @@ fn compare_old_fingerprint(loc: &Path, new_fingerprint: &Fingerprint)
     }
 
     let old_fingerprint_json = paths::read(&loc.with_extension("json"))?;
-    let old_fingerprint = serde_json::from_str(&old_fingerprint_json).chain_error(|| {
-        internal(format!("failed to deserialize json"))
-    })?;
+    let old_fingerprint = serde_json::from_str(&old_fingerprint_json)
+        .chain_err(|| {internal(format!("failed to deserialize json"))})?;
     new_fingerprint.compare(&old_fingerprint)
 }
 
 fn log_compare(unit: &Unit, compare: &CargoResult<()>) {
-    let mut e = match *compare {
+    let ce = match *compare {
         Ok(..) => return,
-        Err(ref e) => &**e,
+        Err(ref e) => e,
     };
-    info!("fingerprint error for {}: {}", unit.pkg, e);
-    while let Some(cause) = e.cargo_cause() {
-        info!("  cause: {}", cause);
-        e = cause;
-    }
-    let mut e = e.cause();
-    while let Some(cause) = e {
+    info!("fingerprint error for {}: {}", unit.pkg, ce);
+
+    for cause in ce.iter() {
         info!("  cause: {}", cause);
-        e = cause.cause();
     }
 }
 
@@ -545,7 +541,7 @@ pub fn parse_dep_info(dep_info: &Path) -> CargoResult<Option<Vec<PathBuf>>> {
         Some(Ok(line)) => line,
         _ => return Ok(None),
     };
-    let pos = line.find(": ").chain_error(|| {
+    let pos = line.find(": ").ok_or_else(|| {
         internal(format!("dep-info not in an understood format: {}",
                          dep_info.display()))
     })?;
@@ -558,7 +554,7 @@ pub fn parse_dep_info(dep_info: &Path) -> CargoResult<Option<Vec<PathBuf>>> {
         while file.ends_with('\\') {
             file.pop();
             file.push(' ');
-            file.push_str(deps.next().chain_error(|| {
+            file.push_str(deps.next().ok_or_else(|| {
                 internal("malformed dep-info format, trailing \\".to_string())
             })?);
         }
@@ -578,7 +574,8 @@ fn dep_info_mtime_if_fresh(dep_info: &Path) -> CargoResult<Option<FileTime>> {
 fn pkg_fingerprint(cx: &Context, pkg: &Package) -> CargoResult<String> {
     let source_id = pkg.package_id().source_id();
     let sources = cx.packages.sources();
-    let source = sources.get(source_id).chain_error(|| {
+
+    let source = sources.get(source_id).ok_or_else(|| {
         internal("missing package source")
     })?;
     source.fingerprint(pkg)
index 344c81b80752e72b76c5b22893d1640525ec2b64..2014b300934b4f626c8acdd28754c832f704136e 100644 (file)
@@ -187,13 +187,12 @@ impl<'a> JobQueue<'a> {
 
                             if self.active > 0 {
                                 error = Some(human("build failed"));
-                                handle_error(&*e, &mut *cx.config.shell());
+                                handle_error(e, &mut *cx.config.shell());
                                 cx.config.shell().say(
                                             "Build failed, waiting for other \
                                              jobs to finish...", YELLOW)?;
                             }
-
-                            if error.is_none() {
+                            else {
                                 error = Some(e);
                             }
                         }
index c827887948fe6867cab89dbc1e927257dd8c49b9..1e2aad9ccd5da29045579234803ee534aa733841 100644 (file)
@@ -11,8 +11,9 @@ use serde_json;
 use core::{Package, PackageId, PackageSet, Target, Resolve};
 use core::{Profile, Profiles, Workspace};
 use core::shell::ColorConfig;
-use util::{self, CargoResult, ProcessBuilder, ProcessError, human, machine_message};
-use util::{Config, internal, ChainError, profile, join_paths, short_hash};
+use util::{self, ProcessBuilder, human, machine_message};
+use util::{Config, internal, profile, join_paths, short_hash};
+use util::errors::{CargoResult, CargoResultExt};
 use util::Freshness;
 
 use self::job::{Job, Work};
@@ -67,7 +68,7 @@ pub trait Executor: Send + Sync + 'static {
     fn init(&self, _cx: &Context) {}
     /// If execution succeeds, the ContinueBuild value indicates whether Cargo
     /// should continue with the build process for this package.
-    fn exec(&self, cmd: ProcessBuilder, _id: &PackageId) -> Result<(), ProcessError> {
+    fn exec(&self, cmd: ProcessBuilder, _id: &PackageId) -> CargoResult<()> {
         cmd.exec()?;
         Ok(())
     }
@@ -77,7 +78,7 @@ pub trait Executor: Send + Sync + 'static {
                  _id: &PackageId,
                  handle_stdout: &mut FnMut(&str) -> CargoResult<()>,
                  handle_stderr: &mut FnMut(&str) -> CargoResult<()>)
-                 -> Result<(), ProcessError> {
+                 -> CargoResult<()> {
         cmd.exec_with_streaming(handle_stdout, handle_stderr)?;
         Ok(())
     }
@@ -333,7 +334,7 @@ fn rustc(cx: &mut Context, unit: &Unit, exec: Arc<Executor>) -> CargoResult<Work
             }
             for dst in &dsts {
                 if fs::metadata(dst).is_ok() {
-                    fs::remove_file(dst).chain_error(|| {
+                    fs::remove_file(dst).chain_err(|| {
                         human(format!("Could not remove file: {}.", dst.display()))
                     })?;
                 }
@@ -367,11 +368,11 @@ fn rustc(cx: &mut Context, unit: &Unit, exec: Arc<Executor>) -> CargoResult<Work
                     }
                     Ok(())
                 }
-            ).chain_error(|| {
+            ).chain_err(|| {
                 human(format!("Could not compile `{}`.", name))
             })?;
         } else {
-            exec.exec(rustc, &package_id).chain_error(|| {
+            exec.exec(rustc, &package_id).map_err(|e| e.to_internal()).chain_err(|| {
                 human(format!("Could not compile `{}`.", name))
             })?;
         }
@@ -382,7 +383,7 @@ fn rustc(cx: &mut Context, unit: &Unit, exec: Arc<Executor>) -> CargoResult<Work
                                             .to_str().unwrap()
                                             .replace(&real_name, &crate_name));
             if src.exists() && src.file_name() != dst.file_name() {
-                fs::rename(&src, &dst).chain_error(|| {
+                fs::rename(&src, &dst).chain_err(|| {
                     internal(format!("could not rename crate {:?}", src))
                 })?;
             }
@@ -390,7 +391,7 @@ fn rustc(cx: &mut Context, unit: &Unit, exec: Arc<Executor>) -> CargoResult<Work
 
         if fs::metadata(&rustc_dep_info_loc).is_ok() {
             info!("Renaming dep_info {:?} to {:?}", rustc_dep_info_loc, dep_info_loc);
-            fs::rename(&rustc_dep_info_loc, &dep_info_loc).chain_error(|| {
+            fs::rename(&rustc_dep_info_loc, &dep_info_loc).chain_err(|| {
                 internal(format!("could not rename dep info: {:?}",
                               rustc_dep_info_loc))
             })?;
@@ -408,7 +409,7 @@ fn rustc(cx: &mut Context, unit: &Unit, exec: Arc<Executor>) -> CargoResult<Work
                        pass_l_flag: bool,
                        current_id: &PackageId) -> CargoResult<()> {
         for key in build_scripts.to_link.iter() {
-            let output = build_state.get(key).chain_error(|| {
+            let output = build_state.get(key).ok_or_else(|| {
                 internal(format!("couldn't find build state for {}/{:?}",
                                  key.0, key.1))
             })?;
@@ -480,7 +481,7 @@ fn link_targets(cx: &mut Context, unit: &Unit, fresh: bool) -> CargoResult<Work>
 
             debug!("linking {} to {}", src.display(), dst.display());
             if dst.exists() {
-                fs::remove_file(&dst).chain_error(|| {
+                fs::remove_file(&dst).chain_err(|| {
                     human(format!("failed to remove: {}", dst.display()))
                 })?;
             }
@@ -489,7 +490,7 @@ fn link_targets(cx: &mut Context, unit: &Unit, fresh: bool) -> CargoResult<Work>
                      debug!("hard link failed {}. falling back to fs::copy", err);
                      fs::copy(src, dst).map(|_| ())
                  })
-                 .chain_error(|| {
+                 .chain_err(|| {
                      human(format!("failed to link or copy `{}` to `{}`",
                                    src.display(), dst.display()))
             })?;
@@ -526,7 +527,7 @@ fn add_plugin_deps(rustc: &mut ProcessBuilder,
     let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
     for id in build_scripts.plugins.iter() {
         let key = (id.clone(), Kind::Host);
-        let output = build_state.get(&key).chain_error(|| {
+        let output = build_state.get(&key).ok_or_else(|| {
             internal(format!("couldn't find libs for plugin dep {}", id))
         })?;
         search_path.append(&mut filter_dynamic_search_path(output.library_paths.iter(),
@@ -627,7 +628,7 @@ fn rustdoc(cx: &mut Context, unit: &Unit) -> CargoResult<Work> {
             }
         }
         state.running(&rustdoc);
-        rustdoc.exec().chain_error(|| {
+        rustdoc.exec().chain_err(|| {
             human(format!("Could not document `{}`.", name))
         })
     }))
index b659965d5b7832f8330a52dcb0c533d70f4dde4b..145b6fc41529c1583be40c9ef8791a539083847c 100644 (file)
@@ -1,7 +1,8 @@
 use std::ffi::{OsString, OsStr};
 
 use ops::{self, Compilation};
-use util::{self, CargoResult, CargoTestError, Test, ProcessError};
+use util::{self, CargoTestError, Test, ProcessError};
+use util::errors::{CargoResult, CargoErrorKind, CargoError};
 use core::Workspace;
 
 pub struct TestOptions<'a> {
@@ -100,7 +101,7 @@ fn run_unit_tests(options: &TestOptions,
             shell.status("Running", cmd.to_string())
         })?;
 
-        if let Err(e) = cmd.exec() {
+        if let Err(CargoError(CargoErrorKind::ProcessErrorKind(e), .. )) = cmd.exec() {
             errors.push(e);
             if !options.no_fail_fast {
                 return Ok((Test::UnitTest(kind.clone(), test.clone()), errors))
@@ -178,7 +179,7 @@ fn run_doc_tests(options: &TestOptions,
             config.shell().verbose(|shell| {
                 shell.status("Running", p.to_string())
             })?;
-            if let Err(e) = p.exec() {
+            if let Err(CargoError(CargoErrorKind::ProcessErrorKind(e), .. )) = p.exec() {
                 errors.push(e);
                 if !options.no_fail_fast {
                     return Ok((Test::Doc, errors));
index 1271cf82d5d9cbb32935d2ffe2051ae1c6d329bb..2f7bedb8b0c78fff63d471a60bd1b4cd53d030ea 100644 (file)
@@ -4,7 +4,8 @@ use toml;
 
 use core::{Resolve, resolver, Workspace};
 use core::resolver::WorkspaceResolve;
-use util::{CargoResult, ChainError, human, Filesystem};
+use util::{human, Filesystem};
+use util::errors::{CargoResult, CargoResultExt};
 use util::toml as cargo_toml;
 
 pub fn load_pkg_lockfile(ws: &Workspace) -> CargoResult<Option<Resolve>> {
@@ -16,15 +17,15 @@ pub fn load_pkg_lockfile(ws: &Workspace) -> CargoResult<Option<Resolve>> {
     let mut f = root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file")?;
 
     let mut s = String::new();
-    f.read_to_string(&mut s).chain_error(|| {
+    f.read_to_string(&mut s).chain_err(|| {
         human(format!("failed to read file: {}", f.path().display()))
     })?;
 
-    (|| {
-        let resolve = cargo_toml::parse(&s, f.path(), ws.config())?;
+    (|| -> CargoResult<Option<Resolve>> {
+        let resolve : toml::Value = cargo_toml::parse(&s, f.path(), ws.config())?;
         let v: resolver::EncodableResolve = resolve.try_into()?;
         Ok(Some(v.into_resolve(ws)?))
-    }).chain_error(|| {
+    })().chain_err(|| {
         human(format!("failed to parse lock file at: {}", f.path().display()))
     })
 }
@@ -98,7 +99,7 @@ pub fn write_pkg_lockfile(ws: &Workspace, resolve: &Resolve) -> CargoResult<()>
         f.file().set_len(0)?;
         f.write_all(out.as_bytes())?;
         Ok(())
-    }).chain_error(|| {
+    }).chain_err(|| {
         human(format!("failed to write {}",
                       ws.root().join("Cargo.lock").display()))
     })
index c2915afcf55398444a37a987fdd26d75e122a1f5..4d55a84ad51c49cb27ac3219a3a25bf88d7a8bc3 100644 (file)
@@ -21,8 +21,9 @@ use ops;
 use sources::{RegistrySource};
 use util::config;
 use util::paths;
-use util::{CargoResult, human, ChainError, ToUrl};
+use util::{human, ToUrl};
 use util::config::{Config, ConfigValue, Location};
+use util::errors::{CargoResult, CargoResultExt};
 use util::important_paths::find_root_manifest_for_wd;
 
 pub struct RegistryConfig {
@@ -200,7 +201,7 @@ pub fn registry(config: &Config,
     };
     let api_host = {
         let mut src = RegistrySource::remote(&sid, config);
-        src.update().chain_error(|| {
+        src.update().chain_err(|| {
             human(format!("failed to update {}", sid))
         })?;
         (src.config()?).unwrap().api
index 2aa60d056dc76173f88981af7f0414da74e1cb90..ca3b87b096cd652befe9b9c6b58a95cfe2a84471 100644 (file)
@@ -5,7 +5,8 @@ use core::registry::PackageRegistry;
 use core::resolver::{self, Resolve, Method};
 use sources::PathSource;
 use ops;
-use util::{profile, human, CargoResult, ChainError};
+use util::{profile, human};
+use util::errors::{CargoResult, CargoResultExt};
 
 /// Resolve all dependencies for the workspace using the previous
 /// lockfile as a guide if present.
@@ -261,7 +262,7 @@ fn add_overrides<'a>(registry: &mut PackageRegistry<'a>,
     for (path, definition) in paths {
         let id = SourceId::for_path(&path)?;
         let mut source = PathSource::new_recursive(&path, &id, ws.config());
-        source.update().chain_error(|| {
+        source.update().chain_err(|| {
             human(format!("failed to update path override `{}` \
                            (defined in `{}`)", path.display(),
                           definition.display()))
index 80a13318f65141b4dce95fba0c64b65cf41fcbd4..4551c7c5976c4b8de3e5322e5693fc0bc4e7f465 100644 (file)
@@ -11,8 +11,9 @@ use url::Url;
 
 use core::{Source, SourceId};
 use sources::ReplacedSource;
-use util::{CargoResult, Config, ChainError, human, ToUrl};
+use util::{Config, human, ToUrl};
 use util::config::ConfigValue;
+use util::errors::{CargoResult, CargoResultExt};
 
 pub struct SourceConfigMap<'cfg> {
     cfgs: HashMap<String, SourceConfig>,
@@ -155,7 +156,7 @@ a lock file compatible with `{orig}` cannot be generated in this situation
         }
 
         let mut srcs = srcs.into_iter();
-        let src = srcs.next().chain_error(|| {
+        let src = srcs.next().ok_or_else(|| {
             human(format!("no source URL specified for `source.{}`, need \
                            either `registry` or `local-registry` defined",
                           name))
@@ -181,7 +182,7 @@ a lock file compatible with `{orig}` cannot be generated in this situation
 
         fn url(cfg: &ConfigValue, key: &str) -> CargoResult<Url> {
             let (url, path) = cfg.string(key)?;
-            url.to_url().chain_error(|| {
+            url.to_url().chain_err(|| {
                 human(format!("configuration key `{}` specified an invalid \
                                URL (in {})", key, path.display()))
 
index ee27a13cec3c831c57cf9029b9b5afbf925e92e7..40dea56c46662ce0b64431fe15970374e0effa15 100644 (file)
@@ -9,7 +9,8 @@ use serde_json;
 
 use core::{Package, PackageId, Summary, SourceId, Source, Dependency, Registry};
 use sources::PathSource;
-use util::{CargoResult, human, ChainError, Config, Sha256};
+use util::{human, Config, Sha256};
+use util::errors::{CargoResult, CargoResultExt, CargoError};
 use util::paths;
 
 pub struct DirectorySource<'cfg> {
@@ -63,7 +64,7 @@ impl<'cfg> Source for DirectorySource<'cfg> {
 
     fn update(&mut self) -> CargoResult<()> {
         self.packages.clear();
-        let entries = self.root.read_dir().chain_error(|| {
+        let entries = self.root.read_dir().chain_err(|| {
             human(format!("failed to read root of directory source: {}",
                           self.root.display()))
         })?;
@@ -112,14 +113,15 @@ impl<'cfg> Source for DirectorySource<'cfg> {
             let pkg = src.root_package()?;
 
             let cksum_file = path.join(".cargo-checksum.json");
-            let cksum = paths::read(&path.join(cksum_file)).chain_error(|| {
+            let cksum = paths::read(&path.join(cksum_file)).chain_err(|| {
                 human(format!("failed to load checksum `.cargo-checksum.json` \
                                of {} v{}",
                               pkg.package_id().name(),
                               pkg.package_id().version()))
 
             })?;
-            let cksum: Checksum = serde_json::from_str(&cksum).chain_error(|| {
+            let cksum: Checksum = serde_json::from_str(&cksum)
+                .map_err(CargoError::from).chain_err(|| {
                 human(format!("failed to decode `.cargo-checksum.json` of \
                                {} v{}",
                               pkg.package_id().name(),
@@ -137,7 +139,7 @@ impl<'cfg> Source for DirectorySource<'cfg> {
     }
 
     fn download(&mut self, id: &PackageId) -> CargoResult<Package> {
-        self.packages.get(id).map(|p| &p.0).cloned().chain_error(|| {
+        self.packages.get(id).map(|p| &p.0).cloned().ok_or_else(|| {
             human(format!("failed to find package with id: {}", id))
         })
     }
@@ -166,7 +168,7 @@ impl<'cfg> Source for DirectorySource<'cfg> {
                         n => h.update(&buf[..n]),
                     }
                 }
-            }).chain_error(|| {
+            })().chain_err(|| {
                 human(format!("failed to calculate checksum of: {}",
                               file.display()))
             })?;
index 7c8050c8f365f71a56ffc3aae4aae3422d0d57a7..d7f5e6852d0f658a2a19a00f1cc1d56946bbaa8a 100644 (file)
@@ -8,7 +8,8 @@ use serde::ser::{self, Serialize};
 use url::Url;
 
 use core::GitReference;
-use util::{CargoResult, ChainError, human, ToUrl, internal, Config, network};
+use util::{human, ToUrl, internal, Config, network};
+use util::errors::{CargoResult, CargoResultExt, CargoError};
 
 #[derive(PartialEq, Clone, Debug)]
 pub struct GitRevision(git2::Oid);
@@ -90,13 +91,13 @@ impl GitRemote {
     pub fn checkout(&self, into: &Path, cargo_config: &Config) -> CargoResult<GitDatabase> {
         let repo = match git2::Repository::open(into) {
             Ok(repo) => {
-                self.fetch_into(&repo, cargo_config).chain_error(|| {
+                self.fetch_into(&repo, cargo_config).chain_err(|| {
                     human(format!("failed to fetch into {}", into.display()))
                 })?;
                 repo
             }
             Err(..) => {
-                self.clone_into(into, cargo_config).chain_error(|| {
+                self.clone_into(into, cargo_config).chain_err(|| {
                     human(format!("failed to clone into: {}", into.display()))
                 })?
             }
@@ -132,7 +133,7 @@ impl GitRemote {
         }
         fs::create_dir_all(dst)?;
         let repo = git2::Repository::init_bare(dst)?;
-        fetch(&repo, &url, "refs/heads/*:refs/heads/*", cargo_config)?;
+        fetch(&repo, &url, "refs/heads/*:refs/heads/*", cargo_config)?;            
         Ok(repo)
     }
 }
@@ -163,23 +164,23 @@ impl GitDatabase {
     pub fn rev_for(&self, reference: &GitReference) -> CargoResult<GitRevision> {
         let id = match *reference {
             GitReference::Tag(ref s) => {
-                (|| {
+                (|| -> CargoResult<git2::Oid> {
                     let refname = format!("refs/tags/{}", s);
                     let id = self.repo.refname_to_id(&refname)?;
                     let obj = self.repo.find_object(id, None)?;
                     let obj = obj.peel(ObjectType::Commit)?;
                     Ok(obj.id())
-                }).chain_error(|| {
+                })().chain_err(|| {
                     human(format!("failed to find tag `{}`", s))
                 })?
             }
             GitReference::Branch(ref s) => {
                 (|| {
                     let b = self.repo.find_branch(s, git2::BranchType::Local)?;
-                    b.get().target().chain_error(|| {
+                    b.get().target().ok_or_else(|| {
                         human(format!("branch `{}` did not have a target", s))
                     })
-                }).chain_error(|| {
+                })().chain_err(|| {
                     human(format!("failed to find branch `{}`", s))
                 })?
             }
@@ -231,20 +232,22 @@ impl<'a> GitCheckout<'a> {
     fn clone_repo(source: &Path, into: &Path) -> CargoResult<git2::Repository> {
         let dirname = into.parent().unwrap();
 
-        fs::create_dir_all(&dirname).chain_error(|| {
+        fs::create_dir_all(&dirname).map_err(CargoError::from).chain_err(|| {
             human(format!("Couldn't mkdir {}", dirname.display()))
         })?;
 
         if fs::metadata(&into).is_ok() {
-            fs::remove_dir_all(into).chain_error(|| {
+            fs::remove_dir_all(into).map_err(CargoError::from).chain_err(|| {
                 human(format!("Couldn't rmdir {}", into.display()))
             })?;
         }
 
         let url = source.to_url()?;
         let url = url.to_string();
-        let repo = git2::Repository::clone(&url, into).chain_error(|| {
-            internal(format!("failed to clone {} into {}", source.display(),
+        let repo = git2::Repository::clone(&url, into)            
+            .map_err(CargoError::from)
+            .chain_err(|| {
+                internal(format!("failed to clone {} into {}", source.display(),
                              into.display()))
         })?;
         Ok(repo)
@@ -281,8 +284,8 @@ impl<'a> GitCheckout<'a> {
         let ok_file = self.location.join(".cargo-ok");
         let _ = fs::remove_file(&ok_file);
         info!("reset {} to {}", self.repo.path().display(), self.revision);
-        let object = self.repo.find_object(self.revision.0, None)?;
-        self.repo.reset(&object, git2::ResetType::Hard, None)?;
+        self.repo.find_object(self.revision.0, None)
+            .and_then(|obj| {self.repo.reset(&obj, git2::ResetType::Hard, None)})?;
         File::create(ok_file)?;
         Ok(())
     }
@@ -294,7 +297,7 @@ impl<'a> GitCheckout<'a> {
             info!("update submodules for: {:?}", repo.workdir().unwrap());
 
             for mut child in repo.submodules()?.into_iter() {
-                update_submodule(repo, &mut child, cargo_config).chain_error(|| {
+                update_submodule(repo, &mut child, cargo_config).chain_err(|| {
                     human(format!("failed to update submodule `{}`",
                                   child.name().unwrap_or("")))
                 })?;
@@ -306,7 +309,7 @@ impl<'a> GitCheckout<'a> {
                             child: &mut git2::Submodule,
                             cargo_config: &Config) -> CargoResult<()> {
             child.init(false)?;
-            let url = child.url().chain_error(|| {
+            let url = child.url().ok_or_else(|| {
                 internal("non-utf8 url for submodule")
             })?;
 
@@ -341,13 +344,13 @@ impl<'a> GitCheckout<'a> {
 
             // Fetch data from origin and reset to the head commit
             let refspec = "refs/heads/*:refs/heads/*";
-            fetch(&repo, url, refspec, cargo_config).chain_error(|| {
+            fetch(&repo, url, refspec, cargo_config).chain_err(|| {
                 internal(format!("failed to fetch submodule `{}` from {}",
                                  child.name().unwrap_or(""), url))
             })?;
 
-            let obj = repo.find_object(head, None)?;
-            repo.reset(&obj, git2::ResetType::Hard, None)?;
+            repo.find_object(head, None)
+                .and_then(|obj| { repo.reset(&obj, git2::ResetType::Hard, None)})?;
             update_submodules(&repo, cargo_config)
         }
     }
@@ -528,7 +531,7 @@ fn with_authentication<T, F>(url: &str, cfg: &git2::Config, mut f: F)
     // In the case of an authentication failure (where we tried something) then
     // we try to give a more helpful error message about precisely what we
     // tried.
-    res.chain_error(|| {
+    res.chain_err(|| {
         let mut msg = "failed to authenticate when downloading \
                        repository".to_string();
         if !ssh_agent_attempts.is_empty() {
@@ -574,6 +577,7 @@ pub fn fetch(repo: &git2::Repository,
 
         network::with_retry(config, || {
             remote.fetch(&[refspec], Some(&mut opts), None)
+                .map_err(network::NetworkError::from)
         })?;
         Ok(())
     })
index 77bf165eb01696f50da7fe6576f1d079242fad31..b50a2fd3e1046607daaa5e8a4b976cb99f7af5d5 100644 (file)
@@ -8,7 +8,7 @@ use glob::Pattern;
 
 use core::{Package, PackageId, Summary, SourceId, Source, Dependency, Registry};
 use ops;
-use util::{self, CargoResult, internal, internal_error, human, ChainError};
+use util::{self, CargoResult, internal, internal_error, human};
 use util::Config;
 
 pub struct PathSource<'cfg> {
@@ -154,7 +154,7 @@ impl<'cfg> PathSource<'cfg> {
                       -> CargoResult<Vec<PathBuf>> {
         warn!("list_files_git {}", pkg.package_id());
         let index = repo.index()?;
-        let root = repo.workdir().chain_error(|| {
+        let root = repo.workdir().ok_or_else(|| {
             internal_error("Can't list files on a bare repository.", "")
         })?;
         let pkg_path = pkg.root();
@@ -230,7 +230,7 @@ impl<'cfg> PathSource<'cfg> {
             if is_dir.unwrap_or_else(|| file_path.is_dir()) {
                 warn!("  found submodule {}", file_path.display());
                 let rel = util::without_prefix(&file_path, root).unwrap();
-                let rel = rel.to_str().chain_error(|| {
+                let rel = rel.to_str().ok_or_else(|| {
                     human(format!("invalid utf-8 filename: {}", rel.display()))
                 })?;
                 // Git submodules are currently only named through `/` path
index 026f20204a9166005117f299d9c2e8938dfbb1b8..d935ee73a0feb95f2e53a81483e90290a8348f37 100644 (file)
@@ -8,7 +8,7 @@ use core::dependency::{Dependency, DependencyInner, Kind};
 use core::{SourceId, Summary, PackageId, Registry};
 use sources::registry::{RegistryPackage, RegistryDependency, INDEX_LOCK};
 use sources::registry::RegistryData;
-use util::{CargoResult, ChainError, internal, Filesystem, Config};
+use util::{CargoResult, internal, Filesystem, Config};
 use util::human;
 
 pub struct RegistryIndex<'cfg> {
@@ -47,7 +47,7 @@ impl<'cfg> RegistryIndex<'cfg> {
         }
         // Ok, we're missing the key, so parse the index file to load it.
         self.summaries(pkg.name(), load)?;
-        self.hashes.get(&key).chain_error(|| {
+        self.hashes.get(&key).ok_or_else(|| {
             internal(format!("no hash listed for {}", pkg))
         }).map(|s| s.clone())
     }
index fa4cbcd82d1f3a87ef33fc3fcc720cc9bf3b3ca5..868e571c86eb5d2af8d79ed4bbdb83f082683c9a 100644 (file)
@@ -8,7 +8,8 @@ use core::PackageId;
 use sources::registry::{RegistryData, RegistryConfig};
 use util::FileLock;
 use util::paths;
-use util::{Config, CargoResult, ChainError, human, Sha256, Filesystem};
+use util::{Config, human, Sha256, Filesystem};
+use util::errors::{CargoResult, CargoResultExt};
 
 pub struct LocalRegistry<'cfg> {
     index_path: Filesystem,
@@ -83,7 +84,7 @@ impl<'cfg> RegistryData for LocalRegistry<'cfg> {
         let mut state = Sha256::new();
         let mut buf = [0; 64 * 1024];
         loop {
-            let n = crate_file.read(&mut buf).chain_error(|| {
+            let n = crate_file.read(&mut buf).chain_err(|| {
                 human(format!("failed to read `{}`", crate_file.path().display()))
             })?;
             if n == 0 {
index 2af4ad9719f6f468e96c1e65cdb8a0d0108c95a9..5319c4bfe6ec2df30f8eabfdfbf01bc4b7053d68 100644 (file)
@@ -168,7 +168,8 @@ use tar::Archive;
 use core::{Source, SourceId, PackageId, Package, Summary, Registry};
 use core::dependency::Dependency;
 use sources::PathSource;
-use util::{CargoResult, Config, internal, ChainError, FileLock, Filesystem};
+use util::{CargoResult, Config, internal, FileLock, Filesystem};
+use util::errors::CargoResultExt;
 use util::hex;
 
 const INDEX_LOCK: &'static str = ".cargo-index-lock";
@@ -359,7 +360,7 @@ impl<'cfg> Source for RegistrySource<'cfg> {
     fn download(&mut self, package: &PackageId) -> CargoResult<Package> {
         let hash = self.index.hash(package, &mut *self.ops)?;
         let path = self.ops.download(package, &hash)?;
-        let path = self.unpack_package(package, &path).chain_error(|| {
+        let path = self.unpack_package(package, &path).chain_err(|| {
             internal(format!("failed to unpack package `{}`", package))
         })?;
         let mut src = PathSource::new(&path, &self.source_id, self.config);
index cac0c7a8be327ec90ffca6ef850c775b8e897766..9661d4490efc0e8cfd22c7bd488488f7c7ef7ea5 100644 (file)
@@ -16,8 +16,8 @@ use sources::git;
 use sources::registry::{RegistryData, RegistryConfig, INDEX_LOCK};
 use util::network;
 use util::{FileLock, Filesystem, LazyCell};
-use util::{Config, CargoResult, ChainError, human, Sha256, ToUrl};
-use util::errors::HttpError;
+use util::{Config, human, Sha256, ToUrl};
+use util::errors::{CargoResult, CargoResultExt};
 
 pub struct RemoteRegistry<'cfg> {
     index_path: Filesystem,
@@ -189,10 +189,11 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
             // git fetch origin master
             let url = self.source_id.url().to_string();
             let refspec = "refs/heads/master:refs/remotes/origin/master";
-            git::fetch(&repo, &url, refspec, self.config).chain_error(|| {
+            git::fetch(&repo, &url, refspec, self.config).chain_err(|| {
                 human(format!("failed to fetch `{}`", url))
             })?;
         }
+
         self.head.set(None);
         *self.tree.borrow_mut() = None;
         Ok(())
@@ -254,7 +255,7 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
             let code = handle.response_code()?;
             if code != 200 && code != 0 {
                 let url = handle.effective_url()?.unwrap_or(&url);
-                Err(HttpError::Not200(code, url.to_string()))
+                Err(network::NetworkErrorKind::HttpNot200(code, url.to_string()).into())
             } else {
                 Ok(())
             }
index 1e682b355f3344fa09c16772bd93d8b13043e8cf..befb37ce519cb75749f65abcd28f79ccd36ccf42 100644 (file)
@@ -1,5 +1,6 @@
 use core::{Source, Registry, PackageId, Package, Dependency, Summary, SourceId};
-use util::{CargoResult, ChainError, human};
+use util::{CargoResult, human};
+use util::errors::CargoResultExt;
 
 pub struct ReplacedSource<'cfg> {
     to_replace: SourceId,
@@ -22,7 +23,7 @@ impl<'cfg> ReplacedSource<'cfg> {
 impl<'cfg> Registry for ReplacedSource<'cfg> {
     fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
         let dep = dep.clone().map_source(&self.to_replace, &self.replace_with);
-        let ret = self.inner.query(&dep).chain_error(|| {
+        let ret = self.inner.query(&dep).chain_err(|| {
             human(format!("failed to query replaced source `{}`",
                           self.to_replace))
         })?;
@@ -38,7 +39,7 @@ impl<'cfg> Source for ReplacedSource<'cfg> {
     }
 
     fn update(&mut self) -> CargoResult<()> {
-        self.inner.update().chain_error(|| {
+        self.inner.update().chain_err(|| {
             human(format!("failed to update replaced source `{}`",
                           self.to_replace))
         })
@@ -46,7 +47,7 @@ impl<'cfg> Source for ReplacedSource<'cfg> {
 
     fn download(&mut self, id: &PackageId) -> CargoResult<Package> {
         let id = id.with_source_id(&self.replace_with);
-        let pkg = self.inner.download(&id).chain_error(|| {
+        let pkg = self.inner.download(&id).chain_err(|| {
             human(format!("failed to download replaced source `{}`",
                           self.to_replace))
         })?;
index a666a07edb0d8acd65eb17279de5a85d156709f6..10455a960489d74befffb9b5641109389eb6b66b 100644 (file)
@@ -38,7 +38,7 @@ struct Parser<'a> {
 }
 
 impl FromStr for Cfg {
-    type Err = Box<CargoError>;
+    type Err = CargoError;
 
     fn from_str(s: &str) -> CargoResult<Cfg> {
         let mut p = Parser::new(s);
@@ -71,7 +71,7 @@ impl CfgExpr {
 }
 
 impl FromStr for CfgExpr {
-    type Err = Box<CargoError>;
+    type Err = CargoError;
 
     fn from_str(s: &str) -> CargoResult<CfgExpr> {
         let mut p = Parser::new(s);
index 269384145b3b2e641da3ccaee7755a4a945de754..120980eede86acfeb9ac7507d96bd9e913c2a2b1 100644 (file)
@@ -15,7 +15,8 @@ use rustc_serialize::{Encodable,Encoder};
 use toml;
 use core::shell::{Verbosity, ColorConfig};
 use core::MultiShell;
-use util::{CargoResult, CargoError, ChainError, Rustc, internal, human};
+use util::{CargoResult, CargoError, Rustc, internal, human};
+use util::errors::CargoResultExt;
 use util::{Filesystem, LazyCell};
 use util::paths;
 
@@ -56,10 +57,10 @@ impl Config {
 
     pub fn default() -> CargoResult<Config> {
         let shell = ::shell(Verbosity::Verbose, ColorConfig::Auto);
-        let cwd = env::current_dir().chain_error(|| {
+        let cwd = env::current_dir().chain_err(|| {
             human("couldn't get the current directory of the process")
         })?;
-        let homedir = homedir(&cwd).chain_error(|| {
+        let homedir = homedir(&cwd).ok_or_else(|| {
             human("Cargo couldn't find your home directory. \
                   This probably means that $HOME was not set.")
         })?;
@@ -100,7 +101,7 @@ impl Config {
     pub fn cargo_exe(&self) -> CargoResult<&Path> {
         self.cargo_exe.get_or_try_init(||
             env::current_exe().and_then(|path| path.canonicalize())
-            .chain_error(|| {
+            .chain_err(|| {
                 human("couldn't get the path to cargo executable")
             })
         ).map(AsRef::as_ref)
@@ -165,7 +166,7 @@ impl Config {
     }
 
     fn get_env<V: FromStr>(&self, key: &str) -> CargoResult<Option<Value<V>>>
-        where Box<CargoError>: From<V::Err>
+        where CargoError: From<V::Err>
     {
         let key = key.replace(".", "_")
                      .replace("-", "_")
@@ -410,26 +411,26 @@ impl Config {
 
         walk_tree(&self.cwd, |mut file, path| {
             let mut contents = String::new();
-            file.read_to_string(&mut contents).chain_error(|| {
+            file.read_to_string(&mut contents).chain_err(|| {
                 human(format!("failed to read configuration file `{}`",
                               path.display()))
             })?;
             let toml = cargo_toml::parse(&contents,
                                          &path,
-                                         self).chain_error(|| {
+                                         self).chain_err(|| {
                 human(format!("could not parse TOML configuration in `{}`",
                               path.display()))
             })?;
-            let value = CV::from_toml(&path, toml).chain_error(|| {
+            let value = CV::from_toml(&path, toml).chain_err(|| {
                 human(format!("failed to load TOML configuration from `{}`",
                               path.display()))
             })?;
-            cfg.merge(value).chain_error(|| {
+            cfg.merge(value).chain_err(|| {
                 human(format!("failed to merge configuration at `{}`",
                               path.display()))
             })?;
             Ok(())
-        }).chain_error(|| human("Couldn't load Cargo configuration"))?;
+        }).chain_err(|| human("Couldn't load Cargo configuration"))?;
 
 
         match cfg {
@@ -541,7 +542,7 @@ impl ConfigValue {
             }
             toml::Value::Table(val) => {
                 Ok(CV::Table(val.into_iter().map(|(key, value)| {
-                    let value = CV::from_toml(path, value).chain_error(|| {
+                    let value = CV::from_toml(path, value).chain_err(|| {
                         human(format!("failed to parse key `{}`", key))
                     })?;
                     Ok((key, value))
@@ -568,7 +569,7 @@ impl ConfigValue {
                         Occupied(mut entry) => {
                             let path = value.definition_path().to_path_buf();
                             let entry = entry.get_mut();
-                            entry.merge(value).chain_error(|| {
+                            entry.merge(value).chain_err(|| {
                                 human(format!("failed to merge key `{}` between \
                                                files:\n  \
                                                file 1: {}\n  \
@@ -757,7 +758,7 @@ fn walk_tree<F>(pwd: &Path, mut walk: F) -> CargoResult<()>
     // Once we're done, also be sure to walk the home directory even if it's not
     // in our history to be sure we pick up that standard location for
     // information.
-    let home = homedir(pwd).chain_error(|| {
+    let home = homedir(pwd).ok_or_else(|| {
         human("Cargo couldn't find your home directory. \
               This probably means that $HOME was not set.")
     })?;
index 0c621524b72de862e5fc3d4b93ecdc84ec171290..2a98a807e785a4be163a0c1630ce6ac503c6546b 100644 (file)
@@ -1,5 +1,4 @@
 use std::error::Error;
-use std::ffi;
 use std::fmt;
 use std::io;
 use std::num;
@@ -8,137 +7,92 @@ use std::str;
 use std::string;
 
 use core::TargetKind;
+use util::network::{NetworkError, NetworkErrorKind};
 
 use curl;
 use git2;
-use glob;
 use semver;
 use serde_json;
 use term;
 use toml;
-use url;
 
-pub type CargoResult<T> = Result<T, Box<CargoError>>;
-
-// =============================================================================
-// CargoError trait
-
-pub trait CargoError: Error + Send + 'static {
-    fn is_human(&self) -> bool { false }
-    fn cargo_cause(&self) -> Option<&CargoError>{ None }
-    fn as_error(&self) -> &Error where Self: Sized { self as &Error }
-}
-
-impl Error for Box<CargoError> {
-    fn description(&self) -> &str { (**self).description() }
-    fn cause(&self) -> Option<&Error> { (**self).cause() }
-}
-
-impl CargoError for Box<CargoError> {
-    fn is_human(&self) -> bool { (**self).is_human() }
-    fn cargo_cause(&self) -> Option<&CargoError> { (**self).cargo_cause() }
-}
-
-// =============================================================================
-// Chaining errors
-
-pub trait ChainError<T> {
-    fn chain_error<E, F>(self, callback: F) -> CargoResult<T>
-                         where E: CargoError, F: FnOnce() -> E;
-}
-
-#[derive(Debug)]
-struct ChainedError<E> {
-    error: E,
-    cause: Box<CargoError>,
-}
-
-impl<'a, T, F> ChainError<T> for F where F: FnOnce() -> CargoResult<T> {
-    fn chain_error<E, C>(self, callback: C) -> CargoResult<T>
-                         where E: CargoError, C: FnOnce() -> E {
-        self().chain_error(callback)
+error_chain! {
+    types {
+        CargoError, CargoErrorKind, CargoResultExt, CargoResult;
     }
-}
 
-impl<T, E: CargoError + 'static> ChainError<T> for Result<T, E> {
-    fn chain_error<E2: 'static, C>(self, callback: C) -> CargoResult<T>
-                         where E2: CargoError, C: FnOnce() -> E2 {
-        self.map_err(move |err| {
-            Box::new(ChainedError {
-                error: callback(),
-                cause: Box::new(err),
-            }) as Box<CargoError>
-        })
+    links {
+        Network(NetworkError, NetworkErrorKind);
     }
-}
 
-impl<T> ChainError<T> for Box<CargoError> {
-    fn chain_error<E2, C>(self, callback: C) -> CargoResult<T>
-                         where E2: CargoError, C: FnOnce() -> E2 {
-        Err(Box::new(ChainedError {
-            error: callback(),
-            cause: self,
-        }))
+    foreign_links {
+        ParseSemver(semver::ReqParseError);
+        Io(io::Error);
+        SerdeJson(serde_json::Error);
+        TomlSer(toml::ser::Error);
+        TomlDe(toml::de::Error);
+        Term(term::Error);
+        ParseInt(num::ParseIntError);
+        ParseBool(str::ParseBoolError);
+        Parse(string::ParseError);
+        Git(git2::Error);
+        Curl(curl::Error);
     }
-}
 
-impl<T> ChainError<T> for Option<T> {
-    fn chain_error<E: 'static, C>(self, callback: C) -> CargoResult<T>
-                         where E: CargoError, C: FnOnce() -> E {
-        match self {
-            Some(t) => Ok(t),
-            None => Err(Box::new(callback())),
+    errors {
+        Internal(description: String, details: Option<String>){
+            description(&description)
+            display("{}", &description)
+        }
+        ProcessErrorKind(proc_err: ProcessError) {
+            description(&proc_err.desc)
+            display("{}", &proc_err.desc)
+        }
+        CargoTestErrorKind(test_err: CargoTestError) {
+            description(&test_err.desc)
+            display("{}", &test_err.desc)
         }
     }
 }
 
-impl<E: Error> Error for ChainedError<E> {
-    fn description(&self) -> &str { self.error.description() }
-}
+impl CargoError {
+    pub fn to_internal(self) -> Self {
+        CargoError(CargoErrorKind::Internal(self.description().to_string(), None), self.1)
+    }
 
-impl<E: fmt::Display> fmt::Display for ChainedError<E> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(&self.error, f)
+    fn is_human(&self) -> bool {
+        match &self.0 {
+            &CargoErrorKind::Msg(_) => true,
+            &CargoErrorKind::TomlSer(_) => true,
+            &CargoErrorKind::TomlDe(_) => true,
+            &CargoErrorKind::Curl(_) => true,
+            &CargoErrorKind::Network(ref net_err) => {
+                match net_err {
+                    &NetworkErrorKind::HttpNot200(_, _) => true,
+                    &NetworkErrorKind::Curl(_) => true,
+                    _ => false
+                }
+            },
+            _ => false
+        }
     }
 }
 
-impl<E: CargoError> CargoError for ChainedError<E> {
-    fn is_human(&self) -> bool { self.error.is_human() }
-    fn cargo_cause(&self) -> Option<&CargoError> { Some(&*self.cause) }
-}
 
 // =============================================================================
 // Process errors
-
+#[derive(Debug)]
 pub struct ProcessError {
     pub desc: String,
     pub exit: Option<ExitStatus>,
     pub output: Option<Output>,
-    cause: Option<Box<CargoError>>,
-}
-
-impl Error for ProcessError {
-    fn description(&self) -> &str { &self.desc }
-    fn cause(&self) -> Option<&Error> {
-        self.cause.as_ref().map(|e| e.as_error())
-    }
-}
-
-impl fmt::Display for ProcessError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(&self.desc, f)
-    }
-}
-impl fmt::Debug for ProcessError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(self, f)
-    }
 }
 
 // =============================================================================
 // Cargo test errors.
 
 /// Error when testcases fail
+#[derive(Debug)]
 pub struct CargoTestError {
     pub test: Test,
     pub desc: String,
@@ -146,6 +100,7 @@ pub struct CargoTestError {
     pub causes: Vec<ProcessError>,
 }
 
+#[derive(Debug)]
 pub enum Test {
     Multiple,
     Doc,
@@ -187,88 +142,6 @@ impl CargoTestError {
     }
 }
 
-impl fmt::Display for CargoTestError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(&self.desc, f)
-    }
-}
-
-impl fmt::Debug for CargoTestError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(self, f)
-    }
-}
-
-impl Error for CargoTestError {
-    fn description(&self) -> &str { &self.desc }
-    fn cause(&self) -> Option<&Error> {
-        self.causes.get(0).map(|s| s as &Error)
-    }
-}
-
-
-// =============================================================================
-// Concrete errors
-
-struct ConcreteCargoError {
-    description: String,
-    detail: Option<String>,
-    cause: Option<Box<Error+Send>>,
-    is_human: bool,
-}
-
-impl fmt::Display for ConcreteCargoError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", self.description)?;
-        if let Some(ref s) = self.detail {
-            write!(f, " ({})", s)?;
-        }
-        Ok(())
-    }
-}
-impl fmt::Debug for ConcreteCargoError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(self, f)
-    }
-}
-
-impl Error for ConcreteCargoError {
-    fn description(&self) -> &str { &self.description }
-    fn cause(&self) -> Option<&Error> {
-        self.cause.as_ref().map(|c| {
-            let e: &Error = &**c; e
-        })
-    }
-}
-
-impl CargoError for ConcreteCargoError {
-    fn is_human(&self) -> bool {
-        self.is_human
-    }
-}
-
-// =============================================================================
-// Human errors
-
-#[derive(Debug)]
-pub struct Human<E>(pub E);
-
-impl<E: Error> Error for Human<E> {
-    fn description(&self) -> &str { self.0.description() }
-    fn cause(&self) -> Option<&Error> { self.0.cause() }
-}
-
-impl<E: fmt::Display> fmt::Display for Human<E> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(&self.0, f)
-    }
-}
-
-impl<E: CargoError> CargoError for Human<E> {
-    fn is_human(&self) -> bool { true }
-    fn cargo_cause(&self) -> Option<&CargoError> { self.0.cargo_cause() }
-}
-
 // =============================================================================
 // CLI errors
 
@@ -276,7 +149,7 @@ pub type CliResult = Result<(), CliError>;
 
 #[derive(Debug)]
 pub struct CliError {
-    pub error: Option<Box<CargoError>>,
+    pub error: Option<CargoError>,
     pub unknown: bool,
     pub exit_code: i32
 }
@@ -303,8 +176,8 @@ impl fmt::Display for CliError {
 }
 
 impl CliError {
-    pub fn new(error: Box<CargoError>, code: i32) -> CliError {
-        let human = error.is_human();
+    pub fn new(error: CargoError, code: i32) -> CliError {
+        let human = &error.is_human();
         CliError { error: Some(error), exit_code: code, unknown: !human }
     }
 
@@ -313,163 +186,17 @@ impl CliError {
     }
 }
 
-impl From<Box<CargoError>> for CliError {
-    fn from(err: Box<CargoError>) -> CliError {
+impl From<CargoError> for CliError {
+    fn from(err: CargoError) -> CliError {
         CliError::new(err, 101)
     }
 }
 
-// =============================================================================
-// NetworkError trait
-
-pub trait NetworkError: CargoError {
-    fn maybe_spurious(&self) -> bool;
-}
-
-impl NetworkError for git2::Error {
-    fn maybe_spurious(&self) -> bool {
-        match self.class() {
-            git2::ErrorClass::Net |
-            git2::ErrorClass::Os => true,
-            _ => false
-        }
-    }
-}
-
-impl NetworkError for curl::Error {
-    fn maybe_spurious(&self) -> bool {
-        self.is_couldnt_connect() ||
-            self.is_couldnt_resolve_proxy() ||
-            self.is_couldnt_resolve_host() ||
-            self.is_operation_timedout() ||
-            self.is_recv_error()
-    }
-}
-
-#[derive(Debug)]
-pub enum HttpError {
-    Not200(u32, String),
-    Curl(curl::Error),
-}
-
-impl fmt::Display for HttpError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            HttpError::Not200(code, ref url) => {
-                write!(f, "failed to get 200 response from `{}`, got {}",
-                       url, code)
-            }
-            HttpError::Curl(ref e) => e.fmt(f),
-        }
-    }
-}
-
-impl Error for HttpError {
-    fn description(&self) -> &str {
-        match *self {
-            HttpError::Not200(..) => "failed to get a 200 response",
-            HttpError::Curl(ref e) => e.description(),
-        }
-    }
-
-    fn cause(&self) -> Option<&Error> {
-        match *self {
-            HttpError::Not200(..) => None,
-            HttpError::Curl(ref e) => e.cause(),
-        }
-    }
-}
-
-impl CargoError for HttpError {
-    fn is_human(&self) -> bool {
-        true
-    }
-}
-
-impl NetworkError for HttpError {
-    fn maybe_spurious(&self) -> bool {
-        match *self {
-            HttpError::Not200(code, ref _url) => {
-                500 <= code && code < 600
-            }
-            HttpError::Curl(ref e) => e.maybe_spurious(),
-        }
-    }
-}
-
-impl From<curl::Error> for HttpError {
-    fn from(err: curl::Error) -> HttpError {
-        HttpError::Curl(err)
-    }
-}
-
-// =============================================================================
-// various impls
-
-macro_rules! from_error {
-    ($($p:ty,)*) => (
-        $(impl From<$p> for Box<CargoError> {
-            fn from(t: $p) -> Box<CargoError> { Box::new(t) }
-        })*
-    )
-}
-
-from_error! {
-    semver::ReqParseError,
-    io::Error,
-    ProcessError,
-    git2::Error,
-    serde_json::Error,
-    curl::Error,
-    CliError,
-    url::ParseError,
-    toml::ser::Error,
-    toml::de::Error,
-    ffi::NulError,
-    term::Error,
-    num::ParseIntError,
-    str::ParseBoolError,
-    glob::PatternError,
-    glob::GlobError,
-}
-
-impl From<string::ParseError> for Box<CargoError> {
-    fn from(t: string::ParseError) -> Box<CargoError> {
-        match t {}
-    }
-}
-
-impl<E: CargoError> From<Human<E>> for Box<CargoError> {
-    fn from(t: Human<E>) -> Box<CargoError> { Box::new(t) }
-}
-
-impl CargoError for semver::ReqParseError {}
-impl CargoError for io::Error {}
-impl CargoError for git2::Error {}
-impl CargoError for serde_json::Error {}
-impl CargoError for curl::Error {}
-impl CargoError for ProcessError {}
-impl CargoError for CargoTestError {}
-impl CargoError for CliError {}
-impl CargoError for toml::ser::Error {
-    fn is_human(&self) -> bool { true }
-}
-impl CargoError for toml::de::Error {
-    fn is_human(&self) -> bool { true }
-}
-impl CargoError for url::ParseError {}
-impl CargoError for ffi::NulError {}
-impl CargoError for term::Error {}
-impl CargoError for num::ParseIntError {}
-impl CargoError for str::ParseBoolError {}
-impl CargoError for glob::PatternError {}
-impl CargoError for glob::GlobError {}
 
 // =============================================================================
 // Construction helpers
 
 pub fn process_error(msg: &str,
-                     cause: Option<Box<CargoError>>,
                      status: Option<&ExitStatus>,
                      output: Option<&Output>) -> ProcessError
 {
@@ -500,7 +227,6 @@ pub fn process_error(msg: &str,
         desc: desc,
         exit: status.cloned(),
         output: output.cloned(),
-        cause: cause,
     };
 
     #[cfg(unix)]
@@ -539,49 +265,22 @@ pub fn process_error(msg: &str,
     }
 }
 
-pub fn internal_error(error: &str, detail: &str) -> Box<CargoError> {
-    Box::new(ConcreteCargoError {
-        description: error.to_string(),
-        detail: Some(detail.to_string()),
-        cause: None,
-        is_human: false
-    })
+pub fn internal_error(error: &str, detail: &str) -> CargoError {
+    CargoErrorKind::Internal(error.to_string(), Some(detail.to_string())).into()
 }
 
-pub fn internal<S: fmt::Display>(error: S) -> Box<CargoError> {
+pub fn internal<S: fmt::Display>(error: S) -> CargoError {
     _internal(&error)
 }
 
-fn _internal(error: &fmt::Display) -> Box<CargoError> {
-    Box::new(ConcreteCargoError {
-        description: error.to_string(),
-        detail: None,
-        cause: None,
-        is_human: false
-    })
+fn _internal(error: &fmt::Display) -> CargoError {
+    CargoErrorKind::Internal(error.to_string(), None).into()
 }
 
-pub fn human<S: fmt::Display>(error: S) -> Box<CargoError> {
+pub fn human<S: fmt::Display>(error: S) -> CargoError {
     _human(&error)
 }
 
-fn _human(error: &fmt::Display) -> Box<CargoError> {
-    Box::new(ConcreteCargoError {
-        description: error.to_string(),
-        detail: None,
-        cause: None,
-        is_human: true
-    })
-}
-
-pub fn caused_human<S, E>(error: S, cause: E) -> Box<CargoError>
-    where S: fmt::Display,
-          E: Error + Send + 'static
-{
-    Box::new(ConcreteCargoError {
-        description: error.to_string(),
-        detail: None,
-        cause: Some(Box::new(cause)),
-        is_human: true
-    })
+fn _human(error: &fmt::Display) -> CargoError {
+    CargoErrorKind::Msg(error.to_string()).into()
 }
index 64151bff5d5ff3bb6cd96c9606f111d70ac1f0b6..a94a81e032ee039e260d133954d85fbd70d43025 100644 (file)
@@ -8,7 +8,8 @@ use fs2::{FileExt, lock_contended_error};
 #[allow(unused_imports)]
 use libc;
 
-use util::{CargoResult, ChainError, Config, human};
+use util::{Config, human};
+use util::errors::{CargoResult, CargoResultExt};
 
 pub struct FileLock {
     f: Option<File>,
@@ -211,7 +212,7 @@ impl Filesystem {
             } else {
                 Err(e)
             }
-        }).chain_error(|| {
+        }).chain_err(|| {
             human(format!("failed to open: {}", path.display()))
         })?;
         match state {
@@ -281,7 +282,7 @@ fn acquire(config: &Config,
 
         Err(e) => {
             if e.raw_os_error() != lock_contended_error().raw_os_error() {
-                return Err(human(e)).chain_error(|| {
+                return Err(human(e)).chain_err(|| {
                     human(format!("failed to lock file: {}", path.display()))
                 })
             }
@@ -290,7 +291,7 @@ fn acquire(config: &Config,
     let msg = format!("waiting for file lock on {}", msg);
     config.shell().err().say_status("Blocking", &msg, CYAN, true)?;
 
-    return block().chain_error(|| {
+    return block().chain_err(|| {
         human(format!("failed to lock file: {}", path.display()))
     });
 
index 9010a11a4c232fb364b2093a4d383299579a214d..14ec2419a41a3424fc6c983293b9911335dcd9ff 100644 (file)
@@ -1,9 +1,8 @@
 pub use self::cfg::{Cfg, CfgExpr};
 pub use self::config::{Config, ConfigValue, homedir};
 pub use self::dependency_queue::{DependencyQueue, Fresh, Dirty, Freshness};
-pub use self::errors::{CargoResult, CargoError, Test, ChainError, CliResult};
+pub use self::errors::{CargoResult, CargoResultExt, CargoError, CargoErrorKind, Test, CliResult};
 pub use self::errors::{CliError, ProcessError, CargoTestError};
-pub use self::errors::{Human, caused_human};
 pub use self::errors::{process_error, internal_error, internal, human};
 pub use self::flock::{FileLock, Filesystem};
 pub use self::graph::Graph;
index e5f22a1c388d2bb32d00d489232d56eb83ae5e80..c8765e6eab092fe30d345d39cc3b41818781eaee 100644 (file)
@@ -1,4 +1,53 @@
-use util::{CargoResult, Config, errors};
+use util::Config;
+use util::errors::CargoResult;
+
+use curl;
+use git2;
+
+// =============================================================================
+// NetworkError chain
+error_chain!{
+    types {
+        NetworkError, NetworkErrorKind, NetworkResultExt, NetworkResult;
+    }
+
+    foreign_links {
+        Git(git2::Error);
+        Curl(curl::Error);
+    }
+
+    errors {
+        HttpNot200(code: u32, url: String) {
+            description("failed to get a 200 response")
+            display("failed to get 200 response from `{}`, got {}", url, code)
+        }
+    }
+}
+
+impl NetworkError {
+    pub fn maybe_spurious(&self) -> bool {
+        match &self.0 {
+            &NetworkErrorKind::Msg(_) => false,
+            &NetworkErrorKind::Git(ref git_err) => {
+                match git_err.class() {
+                    git2::ErrorClass::Net |
+                    git2::ErrorClass::Os => true,
+                    _ => false
+                }
+            }
+            &NetworkErrorKind::Curl(ref curl_err) => {
+                curl_err.is_couldnt_connect() ||
+                    curl_err.is_couldnt_resolve_proxy() ||
+                    curl_err.is_couldnt_resolve_host() ||
+                    curl_err.is_operation_timedout() ||
+                    curl_err.is_recv_error()
+            }
+            &NetworkErrorKind::HttpNot200(code, ref _url)  => {
+                500 <= code && code < 600
+            }
+        }
+    }
+}
 
 /// Wrapper method for network call retry logic.
 ///
@@ -13,9 +62,8 @@ use util::{CargoResult, Config, errors};
 /// use util::network;
 /// cargo_result = network.with_retry(&config, || something.download());
 /// ```
-pub fn with_retry<T, E, F>(config: &Config, mut callback: F) -> CargoResult<T>
-    where F: FnMut() -> Result<T, E>,
-          E: errors::NetworkError
+pub fn with_retry<T, F>(config: &Config, mut callback: F) -> CargoResult<T>
+    where F: FnMut() -> NetworkResult<T>
 {
     let mut remaining = config.net_retry()?;
     loop {
@@ -27,62 +75,17 @@ pub fn with_retry<T, E, F>(config: &Config, mut callback: F) -> CargoResult<T>
                 config.shell().warn(msg)?;
                 remaining -= 1;
             }
-            Err(e) => return Err(Box::new(e)),
+            //todo impl from
+            Err(e) => return Err(e.into()),
         }
     }
 }
 #[test]
 fn with_retry_repeats_the_call_then_works() {
-
-    use std::error::Error;
-    use util::human;
-    use std::fmt;
-
-    #[derive(Debug)]
-    struct NetworkRetryError {
-        error: Box<errors::CargoError>,
-    }
-
-    impl Error for NetworkRetryError {
-        fn description(&self) -> &str {
-            self.error.description()
-        }
-        fn cause(&self) -> Option<&Error> {
-            self.error.cause()
-        }
-    }
-
-    impl NetworkRetryError {
-        fn new(error: &str) -> NetworkRetryError {
-            let error = human(error.to_string());
-            NetworkRetryError {
-                error: error,
-            }
-        }
-    }
-
-    impl fmt::Display for NetworkRetryError {
-        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-            fmt::Display::fmt(&self.error, f)
-        }
-    }
-
-    impl errors::CargoError for NetworkRetryError {
-        fn is_human(&self) -> bool {
-            false
-        }
-    }
-
-    impl errors::NetworkError for NetworkRetryError {
-        fn maybe_spurious(&self) -> bool {
-            true
-        }
-    }
-
-    let error1 = NetworkRetryError::new("one");
-    let error2 = NetworkRetryError::new("two");
-    let mut results: Vec<Result<(), NetworkRetryError>> = vec![Ok(()),
-    Err(error1), Err(error2)];
+    //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry
+    let error1 = NetworkErrorKind::HttpNot200(501, "Uri".to_string()).into();
+    let error2 = NetworkErrorKind::HttpNot200(502, "Uri".to_string()).into();
+    let mut results: Vec<NetworkResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
     let config = Config::default().unwrap();
     let result = with_retry(&config, || results.pop().unwrap());
     assert_eq!(result.unwrap(), ())
index 48273ae9263d5a12176faa06c04b2a19079cc028..d68d5d64b7de9cb5120b0ebd30500add5bdbd5e6 100644 (file)
@@ -5,12 +5,13 @@ use std::fs::OpenOptions;
 use std::io::prelude::*;
 use std::path::{Path, PathBuf, Component};
 
-use util::{human, internal, CargoResult, ChainError};
+use util::{human, internal, CargoResult};
+use util::errors::CargoResultExt;
 
 pub fn join_paths<T: AsRef<OsStr>>(paths: &[T], env: &str) -> CargoResult<OsString> {
     env::join_paths(paths.iter()).or_else(|e| {
         let paths = paths.iter().map(Path::new).collect::<Vec<_>>();
-        internal(format!("failed to join path array: {:?}", paths)).chain_error(|| {
+        Err(internal(format!("failed to join path array: {:?}", paths))).chain_err(|| {
             human(format!("failed to join search paths together: {}\n\
                            Does ${} have an unterminated quote character?",
                           e, env))
@@ -73,7 +74,7 @@ pub fn read(path: &Path) -> CargoResult<String> {
         let mut f = File::open(path)?;
         f.read_to_string(&mut ret)?;
         Ok(ret)
-    })().map_err(human).chain_error(|| {
+    })().map_err(human).chain_err(|| {
         human(format!("failed to read `{}`", path.display()))
     })
 }
@@ -84,7 +85,7 @@ pub fn read_bytes(path: &Path) -> CargoResult<Vec<u8>> {
         let mut f = File::open(path)?;
         f.read_to_end(&mut ret)?;
         Ok(ret)
-    })().map_err(human).chain_error(|| {
+    })().map_err(human).chain_err(|| {
         human(format!("failed to read `{}`", path.display()))
     })
 }
@@ -94,7 +95,7 @@ pub fn write(path: &Path, contents: &[u8]) -> CargoResult<()> {
         let mut f = File::create(path)?;
         f.write_all(contents)?;
         Ok(())
-    })().map_err(human).chain_error(|| {
+    })().map_err(human).chain_err(|| {
         human(format!("failed to write `{}`", path.display()))
     })
 }
@@ -109,7 +110,7 @@ pub fn append(path: &Path, contents: &[u8]) -> CargoResult<()> {
 
         f.write_all(contents)?;
         Ok(())
-    }).chain_error(|| {
+    })().chain_err(|| {
         internal(format!("failed to write `{}`", path.display()))
     })
 }
index 26f872ed459c13302b2e935733cd9fab1c6cb5e3..4cb642b73397d4f7d7919b6976fb2e586c484eed 100644 (file)
@@ -5,7 +5,8 @@ use std::fmt;
 use std::path::Path;
 use std::process::{Command, Stdio, Output};
 
-use util::{CargoResult, ProcessError, process_error, read2};
+use util::{CargoResult, CargoResultExt, CargoError, process_error, read2};
+use util::errors::CargoErrorKind;
 use shell_escape::escape;
 
 #[derive(Clone, PartialEq, Debug)]
@@ -72,61 +73,63 @@ impl ProcessBuilder {
 
     pub fn get_envs(&self) -> &HashMap<String, Option<OsString>> { &self.env }
 
-    pub fn exec(&self) -> Result<(), ProcessError> {
+    pub fn exec(&self) -> CargoResult<()> {
         let mut command = self.build_command();
-        let exit = command.status().map_err(|e| {
-            process_error(&format!("could not execute process `{}`",
-                                   self.debug_string()),
-                          Some(Box::new(e)), None, None)
+        let exit = command.status().chain_err(|| {
+            CargoErrorKind::ProcessErrorKind(
+                process_error(&format!("could not execute process `{}`",
+                                   self.debug_string()), None, None))
         })?;
 
         if exit.success() {
             Ok(())
         } else {
-            Err(process_error(&format!("process didn't exit successfully: `{}`",
+            Err(CargoErrorKind::ProcessErrorKind(process_error(&format!("process didn't exit successfully: `{}`",
                                        self.debug_string()),
-                              None, Some(&exit), None))
+                              Some(&exit), None)).into())
         }
     }
 
     #[cfg(unix)]
-    pub fn exec_replace(&self) -> Result<(), ProcessError> {
+    pub fn exec_replace(&self) -> CargoResult<()> {
         use std::os::unix::process::CommandExt;
 
         let mut command = self.build_command();
         let error = command.exec();
-        Err(process_error(&format!("could not execute process `{}`",
-                                   self.debug_string()),
-                          Some(Box::new(error)), None, None))
+        Err(CargoError::with_chain(error,
+            CargoErrorKind::ProcessErrorKind(
+                process_error(&format!("could not execute process `{}`",
+                self.debug_string()), None, None))))
     }
 
     #[cfg(windows)]
-    pub fn exec_replace(&self) -> Result<(), ProcessError> {
+    pub fn exec_replace(&self) -> CargoResult<()> {
         self.exec()
     }
 
-    pub fn exec_with_output(&self) -> Result<Output, ProcessError> {
+    pub fn exec_with_output(&self) -> CargoResult<Output> {
         let mut command = self.build_command();
 
-        let output = command.output().map_err(|e| {
-            process_error(&format!("could not execute process `{}`",
-                                   self.debug_string()),
-                          Some(Box::new(e)), None, None)
+        let output = command.output().chain_err(|| {
+            CargoErrorKind::ProcessErrorKind(
+                process_error(
+                    &format!("could not execute process `{}`", self.debug_string()),
+                          None, None))
         })?;
 
         if output.status.success() {
             Ok(output)
         } else {
-            Err(process_error(&format!("process didn't exit successfully: `{}`",
+            Err(CargoErrorKind::ProcessErrorKind(process_error(&format!("process didn't exit successfully: `{}`",
                                        self.debug_string()),
-                              None, Some(&output.status), Some(&output)))
+                              Some(&output.status), Some(&output))).into())
         }
     }
 
     pub fn exec_with_streaming(&self,
                                on_stdout_line: &mut FnMut(&str) -> CargoResult<()>,
                                on_stderr_line: &mut FnMut(&str) -> CargoResult<()>)
-                               -> Result<Output, ProcessError> {
+                               -> CargoResult<Output> {
         let mut stdout = Vec::new();
         let mut stderr = Vec::new();
 
@@ -166,10 +169,11 @@ impl ProcessBuilder {
                 }
             })?;
             child.wait()
-        })().map_err(|e| {
-            process_error(&format!("could not execute process `{}`",
-                                   self.debug_string()),
-                          Some(Box::new(e)), None, None)
+        })().chain_err(|| {
+            CargoErrorKind::ProcessErrorKind(
+                process_error(&format!("could not execute process `{}`",
+                    self.debug_string()),
+                None, None))
         })?;
         let output = Output {
             stdout: stdout,
@@ -177,13 +181,14 @@ impl ProcessBuilder {
             status: status,
         };
         if !output.status.success() {
-            Err(process_error(&format!("process didn't exit successfully: `{}`",
+            Err(CargoErrorKind::ProcessErrorKind(process_error(&format!("process didn't exit successfully: `{}`",
                                        self.debug_string()),
-                              None, Some(&output.status), Some(&output)))
+                              Some(&output.status), Some(&output))).into())
         } else if let Some(e) = callback_error {
-            Err(process_error(&format!("failed to parse process output: `{}`",
-                                       self.debug_string()),
-                              Some(Box::new(e)), Some(&output.status), Some(&output)))
+            Err(CargoError::with_chain(e,
+                CargoErrorKind::ProcessErrorKind(
+                    process_error(&format!("failed to parse process output: `{}`",
+                    self.debug_string()), Some(&output.status), Some(&output)))))
         } else {
             Ok(output)
         }
index d401452d712fee08f0da58cfd4600f3d9387569b..9118f2213fe00d87efd6e77d465c7ed43a1fa9ff 100644 (file)
@@ -1,6 +1,6 @@
 use std::path::PathBuf;
 
-use util::{self, CargoResult, internal, ChainError, ProcessBuilder};
+use util::{self, CargoResult, internal, ProcessBuilder};
 
 pub struct Rustc {
     pub path: PathBuf,
@@ -28,10 +28,7 @@ impl Rustc {
         let host = {
             let triple = verbose_version.lines().find(|l| {
                 l.starts_with("host: ")
-            }).map(|l| &l[6..]);
-            let triple = triple.chain_error(|| {
-                internal("rustc -v didn't have a line for `host:`")
-            })?;
+            }).map(|l| &l[6..]).ok_or(internal("rustc -v didn't have a line for `host:`"))?;
             triple.to_string()
         };
 
index 7d8cc16cea6ca70eb2b233de3b900d6f96700ca4..a9f8a5a8c96b9d5b14e8a7a2eafe9b0b920a5db2 100644 (file)
@@ -18,7 +18,8 @@ use core::dependency::{Kind, Platform};
 use core::manifest::{LibKind, Profile, ManifestMetadata};
 use ops::is_bad_artifact_name;
 use sources::CRATES_IO;
-use util::{self, CargoResult, human, ToUrl, ToSemver, ChainError, Config};
+use util::{self, CargoResult, human, ToUrl, ToSemver, Config};
+use util::errors::CargoResultExt;
 
 /// Representation of the projects file layout.
 ///
@@ -189,7 +190,7 @@ in the future.", file.display());
         return Ok(ret)
     }
 
-    Err(first_error).chain_error(|| {
+    Err(first_error).chain_err(|| {
         human("could not parse input as TOML")
     })
 }
@@ -674,7 +675,7 @@ impl TomlManifest {
         let mut warnings = vec![];
 
         let project = me.project.as_ref().or_else(|| me.package.as_ref());
-        let project = project.chain_error(|| {
+        let project = project.ok_or_else(|| {
             human("no `package` or `project` section found.")
         })?;
 
@@ -983,7 +984,7 @@ impl TomlManifest {
                -> CargoResult<Vec<(PackageIdSpec, Dependency)>> {
         let mut replace = Vec::new();
         for (spec, replacement) in self.replace.iter().flat_map(|x| x) {
-            let mut spec = PackageIdSpec::parse(spec).chain_error(|| {
+            let mut spec = PackageIdSpec::parse(spec).chain_err(|| {
                 human(format!("replacements must specify a valid semver \
                                version to replace, but `{}` does not",
                               spec))
@@ -1003,7 +1004,8 @@ impl TomlManifest {
 
             let dep = replacement.to_dependency(spec.name(), cx, None)?;
             let dep = {
-                let version = spec.version().chain_error(|| {
+                let version = spec.version().ok_or_else(||
+                {
                     human(format!("replacements must specify a version \
                                    to replace, but `{}` does not",
                                   spec))
index f7c52dcb61137fcf80678d4e061aac1141fa5e4e..b0daa85f2c9757446319619b1a0a5f8ae2e7b1c1 100644 (file)
@@ -14,6 +14,7 @@ path = "lib.rs"
 
 [dependencies]
 curl = "0.4"
+error-chain = "0.10.0"
 serde = "1.0"
 serde_derive = "1.0"
 serde_json = "1.0"
index df032f349f2a52b4e3adbcc4f9363c7d4472b3d5..05ca8e8de70e134eebeb0cae4b09e4ef7cbda15c 100644 (file)
@@ -1,63 +1,72 @@
 extern crate curl;
 extern crate url;
+#[macro_use]
+extern crate error_chain;
 extern crate serde_json;
 #[macro_use]
 extern crate serde_derive;
 
 use std::collections::HashMap;
-use std::fmt;
 use std::fs::File;
 use std::io::prelude::*;
 use std::io::{self, Cursor};
-use std::result;
 
 use curl::easy::{Easy, List};
 
 use url::percent_encoding::{percent_encode, QUERY_ENCODE_SET};
 
+error_chain! {
+        foreign_links {
+            Curl(curl::Error);
+            Io(io::Error);
+            Json(serde_json::Error);
+        }
+
+        errors {
+            NotOkResponse(code: u32, headers: Vec<String>, body: Vec<u8>){
+                description("failed to get a 200 OK response")
+                display("failed to get a 200 OK response, got {}
+headers:
+    {}
+body:
+{}", code, headers.join("\n    ", ), String::from_utf8_lossy(body))
+            }
+            NonUtf8Body {
+                description("response body was not utf-8")
+                display("response body was not utf-8")
+            }
+            Api(errs: Vec<String>) {
+                display("api errors: {}", errs.join(", "))
+            }
+            Unauthorized {
+                display("unauthorized API access")
+            }
+            TokenMissing{
+                display("no upload token found, please run `cargo login`")
+            }
+            NotFound {
+                display("cannot find crate")
+            }
+        }
+    }
+
 pub struct Registry {
     host: String,
     token: Option<String>,
     handle: Easy,
 }
 
-pub type Result<T> = result::Result<T, Error>;
-
 #[derive(PartialEq, Clone, Copy)]
 pub enum Auth {
     Authorized,
-    Unauthorized
-}
-
-pub enum Error {
-    Curl(curl::Error),
-    NotOkResponse(u32, Vec<String>, Vec<u8>),
-    NonUtf8Body,
-    Api(Vec<String>),
     Unauthorized,
-    TokenMissing,
-    Io(io::Error),
-    NotFound,
-    Json(serde_json::Error),
-}
-
-impl From<serde_json::Error> for Error {
-    fn from(err: serde_json::Error) -> Error {
-        Error::Json(err)
-    }
-}
-
-impl From<curl::Error> for Error {
-    fn from(err: curl::Error) -> Error {
-        Error::Curl(err)
-    }
 }
 
 #[derive(Deserialize)]
 pub struct Crate {
     pub name: String,
     pub description: Option<String>,
-    pub max_version: String
+    pub max_version: String,
 }
 
 #[derive(Serialize)]
@@ -156,7 +165,7 @@ impl Registry {
         //      <json request> (metadata for the package)
         //      <le u32 of tarball>
         //      <source tarball>
-        let stat = tarball.metadata().map_err(Error::Io)?;
+        let stat = tarball.metadata()?;
         let header = {
             let mut w = Vec::new();
             w.extend([
@@ -181,7 +190,7 @@ impl Registry {
 
         let token = match self.token.as_ref() {
             Some(s) => s,
-            None => return Err(Error::TokenMissing),
+            None => return Err(Error::from_kind(ErrorKind::TokenMissing)),
         };
         self.handle.put(true)?;
         self.handle.url(&url)?;
@@ -191,9 +200,7 @@ impl Registry {
         headers.append(&format!("Authorization: {}", token))?;
         self.handle.http_headers(headers)?;
 
-        let body = handle(&mut self.handle, &mut |buf| {
-            body.read(buf).unwrap_or(0)
-        })?;
+        let body = handle(&mut self.handle, &mut |buf| body.read(buf).unwrap_or(0))?;
 
         let response = if body.len() > 0 {
             body.parse::<serde_json::Value>()?
@@ -201,23 +208,19 @@ impl Registry {
             "{}".parse()?
         };
 
-        let invalid_categories: Vec<String> =
-            response.get("warnings")
-                .and_then(|j| j.get("invalid_categories"))
-                .and_then(|j| j.as_array())
-                .map(|x| {
-                    x.iter().flat_map(|j| j.as_str()).map(Into::into).collect()
-                })
-                .unwrap_or_else(Vec::new);
-
-        let invalid_badges: Vec<String> =
-            response.get("warnings")
-                .and_then(|j| j.get("invalid_badges"))
-                .and_then(|j| j.as_array())
-                .map(|x| {
-                    x.iter().flat_map(|j| j.as_str()).map(Into::into).collect()
-                })
-                .unwrap_or_else(Vec::new);
+        let invalid_categories: Vec<String> = response
+            .get("warnings")
+            .and_then(|j| j.get("invalid_categories"))
+            .and_then(|j| j.as_array())
+            .map(|x| x.iter().flat_map(|j| j.as_str()).map(Into::into).collect())
+            .unwrap_or_else(Vec::new);
+
+        let invalid_badges: Vec<String> = response
+            .get("warnings")
+            .and_then(|j| j.get("invalid_badges"))
+            .and_then(|j| j.as_array())
+            .map(|x| x.iter().flat_map(|j| j.as_str()).map(Into::into).collect())
+            .unwrap_or_else(Vec::new);
 
         Ok(Warnings {
             invalid_categories: invalid_categories,
@@ -277,7 +280,7 @@ impl Registry {
         if authorized == Auth::Authorized {
             let token = match self.token.as_ref() {
                 Some(s) => s,
-                None => return Err(Error::TokenMissing),
+                None => return Err(Error::from_kind(ErrorKind::TokenMissing)),
             };
             headers.append(&format!("Authorization: {}", token))?;
         }
@@ -314,48 +317,24 @@ fn handle(handle: &mut Easy,
     match handle.response_code()? {
         0 => {} // file upload url sometimes
         200 => {}
-        403 => return Err(Error::Unauthorized),
-        404 => return Err(Error::NotFound),
-        code => return Err(Error::NotOkResponse(code, headers, body))
+        403 => return Err(Error::from_kind(ErrorKind::Unauthorized)),
+        404 => return Err(Error::from_kind(ErrorKind::NotFound)),
+        code => return Err(Error::from_kind(ErrorKind::NotOkResponse(code, headers, body))),
     }
 
     let body = match String::from_utf8(body) {
         Ok(body) => body,
-        Err(..) => return Err(Error::NonUtf8Body),
+        Err(..) => return Err(Error::from_kind(ErrorKind::NonUtf8Body)),
     };
     match serde_json::from_str::<ApiErrorList>(&body) {
         Ok(errors) => {
-            return Err(Error::Api(errors.errors.into_iter().map(|s| s.detail)
-                                        .collect()))
+            return Err(Error::from_kind(ErrorKind::Api(errors
+                                                           .errors
+                                                           .into_iter()
+                                                           .map(|s| s.detail)
+                                                           .collect())))
         }
         Err(..) => {}
     }
     Ok(body)
 }
-
-impl fmt::Display for Error {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            Error::NonUtf8Body => write!(f, "response body was not utf-8"),
-            Error::Curl(ref err) => write!(f, "http error: {}", err),
-            Error::NotOkResponse(code, ref headers, ref body) => {
-                writeln!(f, "failed to get a 200 OK response, got {}", code)?;
-                writeln!(f, "headers:")?;
-                for header in headers {
-                    writeln!(f, "    {}", header)?;
-                }
-                writeln!(f, "body:")?;
-                writeln!(f, "{}", String::from_utf8_lossy(body))?;
-                Ok(())
-            }
-            Error::Api(ref errs) => {
-                write!(f, "api errors: {}", errs.join(", "))
-            }
-            Error::Unauthorized => write!(f, "unauthorized API access"),
-            Error::TokenMissing => write!(f, "no upload token found, please run `cargo login`"),
-            Error::Io(ref e) => write!(f, "io error: {}", e),
-            Error::NotFound => write!(f, "cannot find crate"),
-            Error::Json(ref e) => write!(f, "json error: {}", e),
-        }
-    }
-}
index 63b441b822b50e867633f09a19ac4b060f7d5038..3a4e556536645d8e6837897b48db573c0d1db7f6 100644 (file)
@@ -15,7 +15,7 @@ use serde_json::{self, Value};
 use url::Url;
 use hamcrest as ham;
 use cargo::util::ProcessBuilder;
-use cargo::util::ProcessError;
+use cargo::util::{CargoError, CargoErrorKind, ProcessError};
 
 use support::paths::CargoPathExt;
 
@@ -719,7 +719,7 @@ impl<'a> ham::Matcher<&'a mut ProcessBuilder> for Execs {
 
         match res {
             Ok(out) => self.match_output(&out),
-            Err(ProcessError { output: Some(ref out), .. }) => {
+            Err(CargoError(CargoErrorKind::ProcessErrorKind(ProcessError { output: Some(ref out), .. }), ..)) => {
                 self.match_output(out)
             }
             Err(e) => {
index ffc0e1421ca0e27d410f9ae1e832bf0751ecf310..4a292e6cb18f79e1d11d38284d233f096db6143c 100644 (file)
@@ -1,5 +1,6 @@
 extern crate cargotest;
 extern crate hamcrest;
+extern crate cargo;
 
 use std::str;
 use std::fs;
@@ -8,6 +9,7 @@ use cargotest::rustc_host;
 use cargotest::support::{project, execs, path2url};
 use cargotest::support::registry::Package;
 use hamcrest::{assert_that, existing_file, existing_dir, is_not};
+use cargo::util::{CargoError, CargoErrorKind};
 
 #[test]
 fn simple() {
@@ -357,11 +359,16 @@ fn output_not_captured() {
             pub fn foo() {}
         ");
 
-    let output = p.cargo_process("doc").exec_with_output().err().unwrap()
-                                                          .output.unwrap();
-    let stderr = str::from_utf8(&output.stderr).unwrap();
-    assert!(stderr.contains("☃"), "no snowman\n{}", stderr);
-    assert!(stderr.contains("unknown start of token"), "no message\n{}", stderr);
+    let error = p.cargo_process("doc").exec_with_output().err().unwrap();
+    if let CargoError(CargoErrorKind::ProcessErrorKind(perr), ..) = error {
+        let output = perr.output.unwrap();
+        let stderr = str::from_utf8(&output.stderr).unwrap();
+    
+        assert!(stderr.contains("☃"), "no snowman\n{}", stderr);
+        assert!(stderr.contains("unknown start of token"), "no message\n{}", stderr);
+    }else{
+        assert!(false, "an error kind other than ProcessErrorKind was encountered\n");
+    }
 }
 
 #[test]
index 1777235e1cfd9083c33f6d2dbf16d54da1165264..0820dfda6b0a0dff58d87b19024e083ef65df6ea 100644 (file)
@@ -1,4 +1,3 @@
-#[macro_use]
 extern crate cargotest;
 extern crate flate2;
 extern crate git2;